/****************************************************************************
 *
 * AI Interface
 * Copyright (c) 2007 Antrix Team
 *
 * This file may be distributed under the terms of the Q Public License
 * as defined by Trolltech ASA of Norway and appearing in the file
 * COPYING included in the packaging of this file.
 *
 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 */

#include "StdAfx.h"

AIInterface::AIInterface()
{
	m_waypoints=NULL;
	m_canMove = true;
	m_destinationX = m_destinationY = m_destinationZ = 0;
	m_nextPosX = m_nextPosY = m_nextPosZ = 0;
	UnitToFollow = NULL;
	FollowDistance = 0.0f;
	m_fallowAngle = M_PI/2;
	m_timeToMove = 0;
	m_timeMoved = 0;
	m_moveTimer = 0;
	m_WayPointsShowing = false;
	m_WayPointsShowBackwards = false;
	m_currentWaypoint = 0;
	m_moveBackward = false;
	m_moveType = 0;
	m_moveRun = false;
	m_moveSprint = false;
	m_moveFly = false;
	m_creatureState = STOPPED;
	m_canCallForHelp = false;
	m_hasCalledForHelp = false;
	m_fleeTimer = 0;
	m_FleeDuration = 0;
	m_canFlee = false;
	m_hasFleed = false;
	m_canRangedAttack = false;
	m_FleeHealth = m_CallForHelpHealth = 0.0f;
	m_AIState = STATE_IDLE;

	m_updateAssist = false;
	m_updateTargets = false;
	m_updateAssistTimer = 1;
	m_updateTargetsTimer = TARGET_UPDATE_INTERVAL;

	m_nextSpell = NULL;
	m_nextTarget = NULL;
	m_Unit = NULL;
	m_PetOwner = NULL;
	m_aiCurrentAgent = AGENT_NULL;
	m_moveSpeed = 0.0f;
	UnitToFear = NULL;
	firstLeaveCombat = true;
	m_outOfCombatRange = 2500;

	tauntedBy = NULL;
	isTaunted = false;
	m_AllowedToEnterCombat = true;
	m_totalMoveTime = 0;
	m_lastFollowX = m_lastFollowY = 0;
	m_FearTimer = 0;
	m_WanderTimer = 0;
	m_totemspelltime = 0;
	m_totemspelltimer = 0;
	m_formationFollowAngle = 0.0f;
	m_formationFollowDistance = 0.0f;
	m_formationLinkTarget = 0;
	m_formationLinkSqlId = 0;
	m_currentHighestThreat = 0;

	b_isAttackableOld = false;
	disable_melee = false;
	next_spell_time = 0;
	m_hasWaypointEvents = false;
}

void AIInterface::Init(Unit *un, AIType at, MovementType mt)
{
	ASSERT(at != AITYPE_PET);

	m_AIType = at;
	m_MovementType = mt;

	m_AIState = STATE_IDLE;
	m_MovementState = MOVEMENTSTATE_STOP;

	m_Unit = un;

	m_moveSpeed = m_Unit->m_runSpeed*0.001f;
	/*if(!m_DefaultMeleeSpell)
	{
		m_DefaultMeleeSpell = new AI_Spell;
		m_DefaultMeleeSpell->entryId = 0;
		m_DefaultMeleeSpell->spellType = 0;
		m_DefaultMeleeSpell->agent = AGENT_MELEE;
		m_DefaultSpell = m_DefaultMeleeSpell;
	}*/
	m_sourceX = un->GetPositionX();
	m_sourceY = un->GetPositionY();
	m_sourceZ = un->GetPositionZ();
	m_guardTimer = getMSTime();
}

AIInterface::~AIInterface()
{

}

void AIInterface::Init(Unit *un, AIType at, MovementType mt, Unit *owner)
{
	ASSERT(at == AITYPE_PET || at == AITYPE_TOTEM);

	m_AIType = at;
	m_MovementType = mt;

	m_AIState = STATE_IDLE;
	m_MovementState = MOVEMENTSTATE_STOP;

	m_Unit = un;
	m_PetOwner = owner;

	m_moveSpeed = m_Unit->m_runSpeed*0.001f;
	m_sourceX = un->GetPositionX();
	m_sourceY = un->GetPositionY();
	m_sourceZ = un->GetPositionZ();
}

void AIInterface::HandleEvent(uint32 event, Unit* pUnit, uint32 misc1)
{
	if(!pUnit || !m_Unit)
		return;
	
	if(m_AIState != STATE_EVADE)
	{
		switch(event)
		{
		case EVENT_ENTERCOMBAT:
			{
				/* send the message */
				if(m_Unit->GetTypeId() == TYPEID_UNIT)
				{
					if(((Creature*)m_Unit)->has_combat_text)
						objmgr.HandleMonsterSayEvent(((Creature*)m_Unit), MONSTER_SAY_EVENT_ENTER_COMBAT);

					CALL_SCRIPT_EVENT(m_Unit, OnCombatStart)(pUnit);
					ScriptSystem->OnCreatureEvent(((Creature*)m_Unit), pUnit, CREATURE_EVENT_ON_ENTER_COMBAT);
				}
				
				// Stop the emote
				m_Unit->SetUInt32Value(UNIT_NPC_EMOTESTATE, 0);
				m_returnX = m_Unit->GetPositionX();
				m_returnY = m_Unit->GetPositionY();
				m_returnZ = m_Unit->GetPositionZ();

				m_moveRun = true; //run to the target

				// dismount if mounted
				m_Unit->SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID, 0);

				if(m_AIState != STATE_ATTACKING)
					StopMovement(0);

				m_AIState = STATE_ATTACKING;
				firstLeaveCombat = true;
				if(pUnit && pUnit->GetInstanceID() == m_Unit->GetInstanceID())
				{
					m_Unit->SetUInt64Value(UNIT_FIELD_TARGET, pUnit->GetGUID());
				}
				if(m_Unit->GetMapMgr()->GetMapInfo() && m_Unit->GetMapMgr()->GetMapInfo()->type == INSTANCE_RAID)
				{
					if(m_Unit->GetTypeId() == TYPEID_UNIT)
					{
						if(static_cast<Creature*>(m_Unit)->GetCreatureName() && static_cast<Creature*>(m_Unit)->GetCreatureName()->Rank == 3)
						{
							 m_Unit->GetMapMgr()->AddCombatInProgress(m_Unit->GetGUID());
						}
					}
				}
			}break;
		case EVENT_LEAVECOMBAT:
			{
				// restart emote
				if(m_Unit->GetTypeId() == TYPEID_UNIT)
				{
					if(static_cast<Creature*>(m_Unit)->original_emotestate)
						m_Unit->SetUInt32Value(UNIT_NPC_EMOTESTATE, static_cast<Creature*>(m_Unit)->original_emotestate);
					
					ScriptSystem->OnCreatureEvent(((Creature*)m_Unit), pUnit, CREATURE_EVENT_ON_LEAVE_COMBAT);
				}

				if(m_AIType == AITYPE_PET && m_Unit->GetGUIDHigh() == HIGHGUID_PET)
				{
					m_AIState = STATE_FOLLOWING;
					UnitToFollow = m_PetOwner;
					FollowDistance = 3.0f;
					m_lastFollowX = m_lastFollowY = 0;
					((Pet*)m_Unit)->SetPetAction(PET_ACTION_FOLLOW);
					HandleEvent(EVENT_FOLLOWOWNER, 0, 0);
				}
				else
				{
					CALL_SCRIPT_EVENT(m_Unit, OnCombatStop)(UnitToFollow);
					m_AIState = STATE_EVADE;
					m_Unit->setAttackTarget(NULL);

					UnitToFollow = NULL;
					FollowDistance = 0.0f;
					m_lastFollowX = m_lastFollowY = 0;
				}

				m_aiTargets.clear();			
				m_fleeTimer = 0;
				m_hasFleed = false;
				m_hasCalledForHelp = false;
				m_nextSpell = NULL;
				SetNextTarget(NULL);

				firstLeaveCombat = false;

				// Scan for a new target before moving back on waypoint path
				Unit * Target = FindTarget();
				if(Target != NULL)
					AttackReaction(Target, 1, 0);
				else
					firstLeaveCombat = true;

				/*SpellEntry* spell = getSpellEntry(2054);
				Affect* aff = new Affect(spell, 6000, m_Unit->GetGUID());
				aff->SetHealPerTick(uint16(m_Unit->GetUInt32Value(UNIT_FIELD_MAXHEALTH)/4), 2000);
				m_Unit->AddAffect(aff);*/
				if(m_Unit->GetMapMgr()->GetMapInfo() && m_Unit->GetMapMgr()->GetMapInfo()->type == INSTANCE_RAID)
				{
					if(m_Unit->GetTypeId() == TYPEID_UNIT)
					{
						if(static_cast<Creature*>(m_Unit)->GetCreatureName() && static_cast<Creature*>(m_Unit)->GetCreatureName()->Rank == 3)
						{
							  m_Unit->GetMapMgr()->RemoveCombatInProgress(m_Unit->GetGUID());
						}
					}
				}

				// Remount if mounted
				if(m_Unit->GetTypeId() == TYPEID_UNIT)
				{
					if(((Creature*)m_Unit)->proto)
						m_Unit->SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID, ((Creature*)m_Unit)->proto->MountedDisplayID);
				}
			}break;
		case EVENT_DAMAGETAKEN:
			{
				CALL_SCRIPT_EVENT(m_Unit, OnDamageTaken)(pUnit, misc1);
				if(!modThreatByPtr(pUnit, misc1))
				{
					m_aiTargets.insert(TargetMap::value_type(pUnit, misc1));
				}
			}break;
		case EVENT_FOLLOWOWNER:
			{
				m_AIState = STATE_FOLLOWING;
				if(m_Unit->GetGUIDHigh() == HIGHGUID_PET)
					((Pet*)m_Unit)->SetPetAction(PET_ACTION_FOLLOW);
				UnitToFollow = m_PetOwner;
				m_lastFollowX = m_lastFollowY = 0;
				FollowDistance = 4.0f;

				m_aiTargets.clear();
				m_fleeTimer = 0;
				m_hasFleed = false;
				m_hasCalledForHelp = false;
				m_nextSpell = NULL;
				SetNextTarget(NULL);
			}break;
		case EVENT_FEAR:
			{   
				CALL_SCRIPT_EVENT(m_Unit, OnFear)(pUnit, 0);
				m_AIState_backup = m_AIState;
				m_AIState = STATE_FEAR;
				StopMovement(1);
				UnitToFollow_backup = UnitToFollow;
				UnitToFollow = NULL;
				m_lastFollowX = m_lastFollowY = 0;
				FollowDistance_backup = FollowDistance;
				FollowDistance = 0.0f;

				m_aiTargets.clear();			
				m_fleeTimer = 0;
				m_hasFleed = false;
				m_hasCalledForHelp = false;
				m_moveRun = true;
				
				// update speed
				m_Unit->m_runSpeed /= 2;
				getMoveFlags();

				m_nextSpell = NULL;

				SetNextTarget(NULL);
			}break;
		case EVENT_UNFEAR:
			{
				// update speed
				m_Unit->m_runSpeed *= 2;
				getMoveFlags();
//				m_AIState = STATE_IDLE;
				//maybe we were not idle before fear. Like a guardian could have been doing something
				m_AIState = m_AIState_backup;
				UnitToFollow = UnitToFollow_backup;
				FollowDistance = FollowDistance_backup;
			}break;
		case EVENT_WANDER:
			{   
				//CALL_SCRIPT_EVENT(m_Unit, OnWander)(pUnit, 0); FIXME
				m_AIState_backup = m_AIState;
				m_AIState = STATE_WANDER;
				StopMovement(1);
				UnitToFollow_backup = UnitToFollow;
				UnitToFollow = NULL;
				m_lastFollowX = m_lastFollowY = 0;
				FollowDistance_backup = FollowDistance;
				FollowDistance = 0.0f;

				//comented by Zack : why do we need to forget our old targets ?. Maybe this was put here with a reason
//				m_aiTargets.clear();
				m_fleeTimer = 0;
				m_hasFleed = false;
				m_hasCalledForHelp = false;
				m_moveRun = true;
				
				// update speed
				m_Unit->m_runSpeed /= 2;
				getMoveFlags();

				m_nextSpell = NULL;

				SetNextTarget(NULL);
				//m_Unit->setAttackTarget(NULL);

			}break;
		case EVENT_UNWANDER:
			{
				// update speed
				m_Unit->m_runSpeed *= 2;
				getMoveFlags();
				UnitToFollow = UnitToFollow_backup;
				FollowDistance = FollowDistance_backup;
				m_AIState = m_AIState_backup;
			}break;	   
		default:
			{
			}break;
		}
	}
	if(event != EVENT_UNITDIED)
		m_Unit->setAttackTarget(m_nextTarget);

	//Should be able to do this stuff even when evading
	switch(event)
	{
		case EVENT_UNITDIED:
		{
			CALL_SCRIPT_EVENT(m_Unit, OnDied)(pUnit);
			ScriptSystem->OnCreatureEvent(((Creature*)m_Unit), pUnit, CREATURE_EVENT_ON_DIED);
			m_AIState = STATE_IDLE;

			StopMovement(0);
			m_aiTargets.clear();
			UnitToFollow = NULL;
			m_lastFollowX = m_lastFollowY = 0;
			UnitToFear = NULL;
			FollowDistance = 0.0f;
			m_fleeTimer = 0;
			m_hasFleed = false;
			m_hasCalledForHelp = false;
			m_nextSpell = NULL;

			SetNextTarget(NULL);
		
			//reset waypoint to 0
			m_currentWaypoint = 0;
			
			// There isn't any need to do any attacker checks here, as
			// they should all be taken care of in DealDamage

			if(m_AIType == AITYPE_PET)
			{
				SetUnitToFollow(m_PetOwner);
				SetFollowDistance(3.0f);
				HandleEvent(EVENT_FOLLOWOWNER, m_Unit, 0);
			}

			if(m_Unit->GetMapMgr() && m_Unit->GetMapMgr()->GetMapInfo() && m_Unit->GetMapMgr()->GetMapInfo()->type == INSTANCE_RAID || m_Unit->GetMapMgr()->GetMapInfo() && m_Unit->GetMapMgr()->GetMapInfo()->type == INSTANCE_MULTIMODE)
            {
                if(m_Unit->GetTypeId() == TYPEID_UNIT && !m_Unit->IsPet())
                {
                    if(static_cast<Creature*>(m_Unit)->GetCreatureName() && static_cast<Creature*>(m_Unit)->GetCreatureName()->Rank == 3)
                    {
                        m_Unit->GetMapMgr()->RemoveCombatInProgress(m_Unit->GetGUID());
                        sInstanceSavingManager.SaveObjectStateToInstance(m_Unit);
                        m_Unit->GetMapMgr()->SavePlayersToInstance();
                    }
                    else if(static_cast<Creature*>(m_Unit)->proto && static_cast<Creature*>(m_Unit)->proto->boss && m_Unit->GetMapMgr()->iInstanceMode == MODE_HEROIC)
                    {
                        sInstanceSavingManager.SaveObjectStateToInstance(m_Unit);
                        m_Unit->GetMapMgr()->SavePlayersToInstance();
                    }
                }
            }
		}break;
	}
}

void AIInterface::Update(uint32 p_time)
{
	if(m_AIType == AITYPE_TOTEM)
	{
		assert(totemspell != 0);
		if(p_time >= m_totemspelltimer)
		{
			Spell * pSpell = new Spell(m_Unit, totemspell, true, 0);
			SpellCastTargets targets(0);
			if(!m_nextTarget ||
				(m_nextTarget && 
					(!m_Unit->GetMapMgr()->GetUnit(m_nextTarget->GetGUID()) || 
					!m_nextTarget->isAlive() ||
					!IsInrange(m_Unit,m_nextTarget,pSpell->GetRadius(0)) || isAttackable(m_Unit, m_nextTarget) != b_isAttackableOld)))
			{
				//something happend to our target, pick another one
				pSpell->GenerateTargets(&targets);
				if(targets.m_targetMask & TARGET_FLAG_UNIT)
				{
					m_nextTarget = m_Unit->GetMapMgr()->GetUnit(targets.m_unitTarget);
					// used to determine when the totem should stop affecting the target
					if(m_nextTarget)
						b_isAttackableOld = true;
				}
				else m_nextTarget=NULL;//sorry but could not find a target
			}
			if(m_nextTarget)
			{
				SpellCastTargets targets(m_nextTarget->GetGUID());
				pSpell->prepare(&targets);
				// need proper cooldown time!
				m_totemspelltimer = m_totemspelltime;
			}
			else delete pSpell;
			// these will *almost always* be AoE, so no need to find a target here.
//			SpellCastTargets targets(m_Unit->GetGUID());
//			Spell * pSpell = new Spell(m_Unit, totemspell, true, 0);
//			pSpell->prepare(&targets);
			// need proper cooldown time!
//			m_totemspelltimer = m_totemspelltime;
		}
		else
		{
			m_totemspelltimer -= p_time;
		}
		return;
	}

	_UpdateTimer(p_time);
	_UpdateTargets();
	if(m_Unit->isAlive() && m_AIState != STATE_IDLE 
		&& m_AIState != STATE_FOLLOWING && m_AIState != STATE_FEAR 
		&& m_AIState != STATE_WANDER && m_AIState != STATE_SCRIPTMOVE)
	{
		if(m_AIType == AITYPE_PET )
		{
			if(!m_Unit->bInvincible && m_Unit->GetGUIDHigh() == HIGHGUID_PET) 
			{
				Pet * pPet = static_cast<Pet*>(m_Unit);
	
				if(pPet->GetPetAction() == PET_ACTION_ATTACK || pPet->GetPetState() != PET_STATE_PASSIVE)
				{
					_UpdateCombat(p_time);
				}
			}
		}
		else
		{
			_UpdateCombat(p_time);
		}
	}

	_UpdateMovement(p_time);

	if(m_AIState == STATE_EVADE)
	{
		if(m_creatureState != MOVING)
		{
			if(m_AIType == AITYPE_PET && m_PetOwner != NULL)
			{
				m_returnX = m_PetOwner->GetPositionX()+(3*(cosf(m_fallowAngle+m_PetOwner->GetOrientation())));
				m_returnY = m_PetOwner->GetPositionY()+(3*(sinf(m_fallowAngle+m_PetOwner->GetOrientation())));
				m_returnZ = m_PetOwner->GetPositionZ();
			}

			if(m_returnX != 0.0f && m_returnY != 0.0f && m_returnZ != 0.0f)
			{
				//return to last position before attacking
				MoveTo(m_returnX,m_returnY,m_returnZ,m_Unit->GetOrientation());
				Creature *aiowner = static_cast<Creature*>(m_Unit);
				if(aiowner)
				{
					//clear tagger.
					aiowner->Tagged = false;
					aiowner->TaggerGuid = 0;
					aiowner->SetUInt32Value(UNIT_DYNAMIC_FLAGS,aiowner->GetUInt32Value(UNIT_DYNAMIC_FLAGS) & ~(U_DYN_FLAG_TAGGED_BY_OTHER |U_DYN_FLAG_LOOTABLE));
				}
			}
		}
		//else
		//{
		if(m_returnX !=0.0f && m_returnY != 0.0f)
		{
			if(m_Unit->GetDistanceSq(m_returnX,m_returnY,m_returnZ) < 4.0f/*2.0*/)
			{
				m_AIState = STATE_IDLE;
				m_returnX = m_returnY = m_returnZ = 0.0f;
				if(hasWaypoints())
				{
					
						if(m_moveBackward)
						{
							if(m_currentWaypoint != GetWayPointsCount()-1)
								m_currentWaypoint++;
						}
						else
						{
							if(m_currentWaypoint != 0)
								m_currentWaypoint--;
						}
			

				}
				// Set health to full if they at there last location before attacking
				if(m_AIType != AITYPE_PET)
					//if(m_Unit->GetMapId() > 1 && m_Unit->GetMapId() != 530)
					m_Unit->SetUInt32Value(UNIT_FIELD_HEALTH,m_Unit->GetUInt32Value(UNIT_FIELD_MAXHEALTH));
			}
		}
		//}
	}
	if(m_fleeTimer)
	{
		if(m_fleeTimer > p_time)
		{
			m_fleeTimer -= p_time;
			_CalcDestinationAndMove(m_nextTarget, 5.0f);
		}
		else
		{
			m_fleeTimer = 0;
			SetNextTarget(FindTargetForSpell(m_nextSpell));
		}
	}


	//Pet Dismiss after a certian ditance away
	/*if(m_AIType == AITYPE_PET && m_PetOwner != NULL)
	{
		float dist = (m_Unit->GetInstanceID() == m_PetOwner->GetInstanceID()) ? 
			m_Unit->GetDistanceSq(m_PetOwner) : 99999.0f;

		if(dist > 8100.0f) //90 yard away we Dismissed
		{
			DismissPet();
			return;
		}
	}*/
}

void AIInterface::_UpdateTimer(uint32 p_time)
{
	if(m_updateAssistTimer > p_time)
	{
		m_updateAssistTimer -= p_time;
	}else
	{
		m_updateAssist = true;
		m_updateAssistTimer = TARGET_UPDATE_INTERVAL * 2 - m_updateAssistTimer - p_time;
	}

	if(m_updateTargetsTimer > p_time)
	{
		m_updateTargetsTimer -= p_time;
	}else
	{
		m_updateTargets = true;
		m_updateTargetsTimer = TARGET_UPDATE_INTERVAL * 2 - m_updateTargetsTimer - p_time;
	}
}

void AIInterface::_UpdateTargets()
{
	if(m_Unit->IsPlayer() || (m_AIType != AITYPE_PET && disable_melee))
		return;
	if(((Creature*)m_Unit)->GetCreatureName() && ((Creature*)m_Unit)->GetCreatureName()->Type == CRITTER)
		return;

	AssistTargetSet::iterator i, i2;
	TargetMap::iterator itr, itr2;

	// Find new Assist Targets and remove old ones
	if(m_AIState == STATE_FLEEING)
	{
		FindFriends(100.0f/*10.0*/);
	}
	else if(m_AIState != STATE_IDLE && m_AIState != STATE_SCRIPTIDLE)
	{
		FindFriends(16.0f/*4.0f*/);
	}

	if(m_updateAssist)
	{
		m_updateAssist = false;
		//modified for vs2005 compatibility
		for(i = m_assistTargets.begin(); i != m_assistTargets.end();)
		{
			i2 = i;
			++i;
			if(m_Unit->GetDistanceSq((*i2)) > 2500.0f/*50.0f*/ || !(*i2)->isAlive() || !(*i2)->isInCombat())
			{
				m_assistTargets.erase(i2);
			}
		}
	}

	if(m_updateTargets)
	{
		m_updateTargets = false;
		//modified for vs2005 compatibility
		for(itr = m_aiTargets.begin(); itr != m_aiTargets.end();)
		{
			itr2 = itr;
			++itr;

			//if(!itr->target)		// we shouldnt get to here, i'm guessing.
				//continue;

			if(!itr2->first->isAlive() || m_Unit->GetDistanceSq(itr2->first) >= 6400.0f/*80.0f*/)
			{
				m_aiTargets.erase(itr2);
			}
		}
		if(m_aiTargets.size() == 0 
			&& m_AIState != STATE_IDLE && m_AIState != STATE_FOLLOWING 
			&& m_AIState != STATE_EVADE && m_AIState != STATE_FEAR 
			&& m_AIState != STATE_WANDER && m_AIState != STATE_SCRIPTIDLE)
		{
			if(firstLeaveCombat)
			{
				Unit* target = FindTarget();
				if(target)
				{
					AttackReaction(target, 1, 0);
				}else
				{
					firstLeaveCombat = false;
				}
			}
			/*else
			{
				HandleEvent(EVENT_LEAVECOMBAT, m_Unit, 0);
			}*/
		}
		else if(m_aiTargets.size() == 0 && m_AIType == AITYPE_PET && m_Unit->GetGUIDHigh() == HIGHGUID_PET && static_cast<Pet*>(m_Unit)->GetPetState() == PET_STATE_AGGRESSIVE)
		{
			 Unit* target = FindTarget();
			 if(target)
				 AttackReaction(target, 1, 0);

		}
	}
	// Find new Targets when we are ooc
	if((m_AIState == STATE_IDLE || m_AIState == STATE_SCRIPTIDLE) && m_assistTargets.size() == 0)
	{
		Unit* target = FindTarget();
		if(target)
		{
			AttackReaction(target, 1, 0);
		}
	}
}

///====================================================================
///  Desc: Updates Combat Status of m_Unit
///====================================================================
void AIInterface::_UpdateCombat(uint32 p_time)
{
	if(m_AIType != AITYPE_PET && disable_melee)
		return;

	uint16 agent = m_aiCurrentAgent;

	// If creature is very far from spawn point return to spawnpoint
	// If at instance dont return -- this is wrong ... instance creatures always returns to spawnpoint, dunno how do you got this ideia. 

	if(m_AIType != AITYPE_PET 
		&& (m_outOfCombatRange && m_Unit->GetDistanceSq(m_returnX,m_returnY,m_returnZ) > m_outOfCombatRange) 
		&& m_AIState != STATE_EVADE
		&& m_AIState != STATE_SCRIPTMOVE)
	{
		HandleEvent(EVENT_LEAVECOMBAT, m_Unit, 0);
	}
	else if(!m_nextTarget && m_AIState != STATE_FOLLOWING && m_AIState != STATE_SCRIPTMOVE)
	{
		SetNextTarget(FindTargetForSpell(m_nextSpell));
		if(!m_nextTarget)
		{
			HandleEvent(EVENT_LEAVECOMBAT, m_Unit, 0);
		}
	}

	bool cansee;
	if(m_nextTarget && m_nextTarget->GetInstanceID() == m_Unit->GetInstanceID())
	{
		if(m_Unit->GetTypeId() == TYPEID_UNIT)
			cansee=((Creature*)m_Unit)->CanSee(m_nextTarget);
		else
			cansee = ((Player*)m_Unit)->CanSee(m_nextTarget);
	}
	else 
		cansee=false;

	if(cansee && m_nextTarget && m_nextTarget->isAlive() && m_AIState != STATE_EVADE && !m_Unit->isCasting())
	{
		if(agent == AGENT_NULL)
		{
			if(!m_nextSpell)
				m_nextSpell = this->getSpell();

			if(m_canFlee && !m_hasFleed 
				&& ((m_Unit->GetUInt32Value(UNIT_FIELD_HEALTH) / m_Unit->GetUInt32Value(UNIT_FIELD_MAXHEALTH)) < m_FleeHealth ))
				agent = AGENT_FLEE;
			else if(m_canCallForHelp 
				&& !m_hasCalledForHelp 
				&& (m_CallForHelpHealth > (m_Unit->GetUInt32Value(UNIT_FIELD_HEALTH) / (m_Unit->GetUInt32Value(UNIT_FIELD_MAXHEALTH) > 0 ? m_Unit->GetUInt32Value(UNIT_FIELD_MAXHEALTH) : 1))))
				agent = AGENT_CALLFORHELP;
			else if(m_nextSpell)
			{
				if(m_nextSpell->agent != AGENT_NULL)
				{
					agent = m_nextSpell->agent;
				}
				else
				{
					agent = AGENT_MELEE;
				}
			}
			else
			{
				agent = AGENT_MELEE;
			}
		}
		if(agent == AGENT_RANGED || agent == AGENT_MELEE)
		{
			if(m_canRangedAttack)
			{
				agent = AGENT_MELEE;
				if(m_nextTarget->GetTypeId() == TYPEID_PLAYER)
				{
					float dist = m_Unit->GetDistanceSq(m_nextTarget);
					if(((Player*)m_nextTarget)->m_currentMovement == MOVE_ROOT || dist >= 64.0f)
					{
						agent =  AGENT_RANGED;
					}
				}
				else if(m_nextTarget->m_canMove == false || m_Unit->GetDistanceSq(m_nextTarget) >= 64.0f)
				{
				   agent = AGENT_RANGED;
				}
			}
			else
			{
				agent = AGENT_MELEE;
			}
		}

		if(this->disable_melee && agent == AGENT_MELEE)
			agent = AGENT_NULL;
		  
		switch(agent)
		{
		case AGENT_MELEE:
			{
				float combatReach[2]; // Calculate Combat Reach
				float distance = m_Unit->CalcDistance(m_nextTarget);

				combatReach[0] = 0.0f;
				combatReach[1] = _CalcCombatRange(m_nextTarget, false);

				if(distance >= combatReach[0] && distance <= combatReach[1]) // Target is in Range -> Attack
				{
					if(UnitToFollow != NULL)
					{
						UnitToFollow = NULL; //we shouldn't be following any one
						m_lastFollowX = m_lastFollowY = 0;
						m_Unit->setAttackTarget(NULL);  // remove ourselves from any target that might have been followed
					}
					
					FollowDistance = 0.0f;
					m_moveRun = false;
					//FIXME: offhand shit
					if(m_Unit->isAttackReady(false) && !m_fleeTimer)
					{
						m_creatureState = ATTACKING;
						bool infront = m_Unit->isInFront(m_nextTarget);

						if(!infront) // set InFront
						{
							//prevent mob from rotating while stunned
							if(!m_Unit->IsStunned ())
							{
								setInFront(m_nextTarget);
								infront = true;
							}							
						}
						if(infront)
						{
							m_Unit->setAttackTimer(0, false);
							m_Unit->Strike(m_nextTarget, (agent==AGENT_MELEE)?0:2,NULL,0,0,0, false);
						}
					}
				}
				else // Target out of Range -> Run to it
				{
					//calculate next move
					float dist = 1.0f;
					dist = combatReach[1]-1.15f;
					m_moveRun = true;
					_CalcDestinationAndMove(m_nextTarget, dist);
				}
			}break;
		case AGENT_RANGED:
			{
				float combatReach[2]; // Calculate Combat Reach
				float distance = m_Unit->CalcDistance(m_nextTarget);

				combatReach[0] = 8.0f;
				combatReach[1] = 30.0f;

				if(distance >= combatReach[0] && distance <= combatReach[1]) // Target is in Range -> Attack
				{
					if(UnitToFollow != NULL)
					{
						UnitToFollow = NULL; //we shouldn't be following any one
						m_lastFollowX = m_lastFollowY = 0;
						m_Unit->setAttackTarget(NULL);  // remove ourselves from any target that might have been followed
					}
					
					FollowDistance = 0.0f;
					m_moveRun = false;
					//FIXME: offhand shit
					if(m_Unit->isAttackReady(false) && !m_fleeTimer)
					{
						m_creatureState = ATTACKING;
						bool infront = m_Unit->isInFront(m_nextTarget);

						if(!infront) // set InFront
						{
							//prevent mob from rotating while stunned
							if(!m_Unit->IsStunned ())
							{
								setInFront(m_nextTarget);
								infront = true;
							}							
						}

						if(infront)
						{
							m_Unit->setAttackTimer(0, false);
							SpellEntry *info = sSpellStore.LookupEntry(SPELL_RANGED_BOW);
							if(info)
							{
								Spell *sp = new Spell(m_Unit, info, false, NULL);
								SpellCastTargets targets;
								targets.m_unitTarget = m_nextTarget->GetGUID();
								sp->prepare(&targets);
								//Lets make spell handle this
								//m_Unit->Strike(m_nextTarget, (agent==AGENT_MELEE)?0:2,NULL,0,0,0);
							}
						}
					}
				}
				else // Target out of Range -> Run to it
				{
					//calculate next move
					float dist = 1.0f;

					if(distance < combatReach[0])// Target is too near
						dist = 9.0f;
					else
						dist = 20.0f;

					m_moveRun = true;
					_CalcDestinationAndMove(m_nextTarget, dist);
				}
			}break;
		case AGENT_SPELL:
			{
				if(!m_nextSpell || !m_nextTarget)
					return;  // this shouldnt happen

				/* stop moving so we don't interrupt the spell */
				//this the way justly suggested
//				if(m_nextSpell->spell->CastingTimeIndex != 1)
				//do not stop for instant spells
				SpellCastTime *sd = sCastTime.LookupEntry(m_nextSpell->spell->CastingTimeIndex);
				if(GetCastTime(sd) != 0)
					StopMovement(0);

				float distance = m_Unit->GetDistanceSq(m_nextTarget);
				if((distance <= powf(m_nextSpell->maxrange,2)  && distance >= powf(m_nextSpell->minrange,2)) || m_nextSpell->maxrange == 0) // Target is in Range -> Attack
				{
					SpellEntry* spellInfo = m_nextSpell->spell;
					SpellCastTargets targets = setSpellTargets(spellInfo, m_nextTarget);
					CastSpell(m_Unit, spellInfo, targets);
					AddSpellCooldown(spellInfo, m_nextSpell);
					m_nextSpell = NULL;
				}
				else // Target out of Range -> Run to it
				{
					//calculate next move
					m_moveRun = true;
					if(m_nextSpell->maxrange < 5.0f)
						_CalcDestinationAndMove(m_nextTarget, 0.0f);
					else
						_CalcDestinationAndMove(m_nextTarget, m_nextSpell->maxrange - 5.0f);
					/*Destination* dst = _CalcDestination(m_nextTarget, dist);
					MoveTo(dst->x, dst->y, dst->z,0);
					delete dst;*/
				}
			}break;
		case AGENT_FLEE:
			{
				//float dist = 5.0f;

				m_moveRun = false;
				if(m_fleeTimer == 0)
					m_fleeTimer = m_FleeDuration;

				/*Destination* dst = _CalcDestination(m_nextTarget, dist);
				MoveTo(dst->x, dst->y, dst->z,0);
				delete dst;*/
				_CalcDestinationAndMove(m_nextTarget, 5.0f);
				if(!m_hasFleed) // to avoid lua excuting spam
					CALL_SCRIPT_EVENT(m_Unit, OnFlee)(m_nextTarget);

				m_AIState = STATE_FLEEING;
				m_nextTarget = m_Unit;

				m_Unit->SetUInt64Value(UNIT_FIELD_TARGET, 0);
				m_Unit->setAttackTarget(0);
				m_Unit->clearAttackers(true);

				WorldPacket data(SMSG_MESSAGECHAT, 100);
				string msg = "%s attempts to run away in fear!";
				data << (uint8)CHAT_MSG_MONSTER_EMOTE;
				data << (uint32)LANG_UNIVERSAL;
				data << (uint32)(strlen(((Creature*)m_Unit)->GetCreatureName()->Name) + 1);
				data << ((Creature*)m_Unit)->GetCreatureName()->Name;
				data << (uint64)0;
				data << (uint32)(msg.size() + 1);
				data << msg;
				data << uint8(0);

				m_Unit->SendMessageToSet(&data, false);

				//m_Unit->SendChatMessage(CHAT_MSG_MONSTER_EMOTE, LANG_UNIVERSAL, msg);
				//sChatHandler.FillMessageData(&data, CHAT_MSG_MONSTER_EMOTE, LANG_UNIVERSAL, msg, m_Unit->GetGUID());			   
			   
				m_hasFleed = true;
			}break;
		case AGENT_CALLFORHELP:
			{
				FindFriends(400.0f/*20.0f*/);
				m_hasCalledForHelp = true; // We only want to call for Help once in a Fight.
				CALL_SCRIPT_EVENT(m_Unit, OnCallForHelp)();
			}break;
		}
	}
	else  if(!m_nextTarget || m_nextTarget->GetInstanceID() != m_Unit->GetInstanceID() || !m_nextTarget->isAlive() || !cansee)
	{
		SetNextTarget(NULL);
		// no more target
		//m_Unit->setAttackTarget(NULL);
	}
}

void AIInterface::DismissPet()
{
	/*
	if(m_AIType != AITYPE_PET)
		return;

	if(!m_PetOwner)
		return;
	
	if(m_PetOwner->GetTypeId() != TYPEID_PLAYER)
		return;

	if(m_Unit->GetUInt32Value(UNIT_CREATED_BY_SPELL) == 0)
		((Player*)m_PetOwner)->SetFreePetNo(false, (int)m_Unit->GetUInt32Value(UNIT_FIELD_PETNUMBER));
	((Player*)m_PetOwner)->SetPet(NULL);
	((Player*)m_PetOwner)->SetPetName("");
	
	//FIXME:Check hunter pet or not
	//FIXME:Check enslaved creature
	m_PetOwner->SetUInt64Value(UNIT_FIELD_SUMMON, 0);
	
	WorldPacket data;
	data.Initialize(SMSG_PET_SPELLS);
	data << (uint64)0;
	((Player*)m_PetOwner)->GetSession()->SendPacket(&data);
	
	sEventMgr.RemoveEvents(((Creature*)m_Unit));
	if(m_Unit->IsInWorld())
	{
		m_Unit->RemoveFromWorld();
	}
	//setup an event to delete the Creature
	sEventMgr.AddEvent(((Creature*)this->m_Unit), &Creature::DeleteMe, EVENT_DELETE_TIMER, 1, 1);*/
}

void AIInterface::AttackReaction(Unit* pUnit, uint32 damage_dealt, uint32 spellId)
{
	if(m_AIState == STATE_EVADE || m_fleeTimer != 0 || !pUnit 
		|| m_Unit->IsPacified() || m_Unit->IsStunned() || !m_Unit->isAlive())
	{
		return;
	}
	if(m_Unit == pUnit)
	{
		return;
	}

	if(m_AIState == STATE_IDLE || m_AIState == STATE_FOLLOWING)
	{
		WipeTargetList();
		
		HandleEvent(EVENT_ENTERCOMBAT, pUnit, 0);
	}

	HandleEvent(EVENT_DAMAGETAKEN, pUnit, _CalcThreat(damage_dealt, spellId, pUnit));
}

bool AIInterface::HealReaction(Unit* caster, Unit* victim, uint32 amount)
{
	if(!caster || !victim)
	{
		printf("!!!BAD POINTER IN AIInterface::HealReaction!!!\n");
		return false;
	}

	int casterInList = 0, victimInList = 0;

	if(m_aiTargets.find(caster) != m_aiTargets.end())
		casterInList = 1;

	if(m_aiTargets.find(victim) != m_aiTargets.end())
		victimInList = 1;

	/*for(i = m_aiTargets.begin(); i != m_aiTargets.end(); i++)
	{
		if(casterInList && victimInList)
		{ // no need to check the rest, just break that
			break;
		}
		if(i->target == victim)
		{
			victimInList = true;
		}
		if(i->target == caster)
		{
			casterInList = true;
		}
	}*/
	if(!victimInList && !casterInList) // none of the Casters is in the Creatures Threat list
	{
		return false;
	}
	if(!casterInList && victimInList) // caster is not yet in Combat but victim is
	{
		// get caster into combat if he's hostile
		if(isHostile(m_Unit, caster))
		{
			//AI_Target trgt;
			//trgt.target = caster;
			//trgt.threat = amount;
			//m_aiTargets.push_back(trgt);
			m_aiTargets.insert(TargetMap::value_type(caster, amount));
			return true;
		}
		return false;
	}
	else if(casterInList && victimInList) // both are in combat already
	{
		// mod threat for caster
		modThreatByPtr(caster, amount);
		return true;
	}
	else // caster is in Combat already but victim is not
	{
		modThreatByPtr(caster, amount);
		// both are players so they might be in the same group
		if(caster->GetTypeId() == TYPEID_PLAYER && victim->GetTypeId() == TYPEID_PLAYER)
		{
			if(((Player*)caster)->GetGroup() == ((Player*)victim)->GetGroup())
			{
				// get victim into combat since they are both
				// in the same party
				if(isHostile(m_Unit, victim))
				{
					m_aiTargets.insert(TargetMap::value_type(victim, 1));
					return true;
				}
				return false;
			}
		}
	}

	return false;
}

void AIInterface::OnDeath(Object* pKiller)
{
	if(pKiller->GetTypeId() == TYPEID_PLAYER || pKiller->GetTypeId() == TYPEID_UNIT)
		HandleEvent(EVENT_UNITDIED, static_cast<Unit*>(pKiller), 0);
	else
		HandleEvent(EVENT_UNITDIED, m_Unit, 0);
}

Unit* AIInterface::FindTarget()
{// find nearest hostile Target to attack
	if( !m_AllowedToEnterCombat ) 
		return NULL;

	Unit* target = NULL;
	Unit* critterTarget = NULL;
	float distance = 999999.0f; // that should do it.. :p
	float crange;
	float z_diff;

	std::set<Object*>::iterator itr, it2;
	Object *pObj;
	Unit *pUnit;
	float dist;
	//target is immune to all form of attacks, cant attack either.
	if(m_Unit->HasFlag(UNIT_FIELD_FLAGS, U_FIELD_FLAG_UNIT_UNTACKABLE_NO_SELECT))
	{
		return 0;
	}

	for( itr = m_Unit->GetInRangeOppFactsSetBegin(); itr != m_Unit->GetInRangeOppFactsSetEnd(); )
	{
		it2 = itr;
		++itr;

		pObj = (*it2);
		if(pObj->GetTypeId() == TYPEID_PLAYER)
		{
			if(static_cast<Player*>(pObj)->GetTaxiState())	  // skip players on taxi
				continue;
		}
		else if(pObj->GetTypeId() != TYPEID_UNIT)
				continue;

		pUnit = static_cast<Unit*>(pObj);
		if(pUnit->bInvincible)
			continue;

		// don't agro players on flying mounts
		/*if(pUnit->GetTypeId() == TYPEID_PLAYER && static_cast<Player*>(pUnit)->FlyCheat)
			continue;*/

		//target is immune to unit attacks however can be targeted
		//as a part of AI we allow this mode to attack creatures as seen many times on oficial.
		if(m_Unit->HasFlag(UNIT_FIELD_FLAGS, U_FIELD_FLAG_UNIT_UNTACKABLE_SELECT) || m_Unit->HasFlag(UNIT_FIELD_FLAGS, U_FIELD_FLAG_UNIT_UNTACKABLE_SELECT_2))
		{
			if(pUnit->IsPlayer() || pUnit->IsPet())
			{
				continue;
			}
		}
		crange = _CalcCombatRange(pUnit,false);
		z_diff = fabs(m_Unit->GetPositionZ() - pUnit->GetPositionZ());
		if(z_diff > crange)
		{
			continue;
		}

		if(pUnit->m_invisible) // skip invisible units
			continue;
		
		if(!pUnit->isAlive()
			|| m_Unit == pUnit /* wtf? */
			|| m_Unit->GetUInt64Value(UNIT_FIELD_CREATEDBY) == pUnit->GetGUID())
			continue;

		dist = m_Unit->GetDistanceSq(pUnit);
		if(!pUnit->m_faction || !pUnit->m_factionDBC)
			continue;

		if(pUnit->m_faction->Faction == 28)// only Attack a critter if there is no other Enemy in range
		{
			if(dist < 225.0f)	// was 10
				critterTarget = pUnit;
			continue;
		}

		if(dist > distance)	 // we want to find the CLOSEST target
			continue;
		
		if(dist <= _CalcAggroRange(pUnit) )
		{
			distance = dist;
			target = pUnit;		
		}
	}

	if( !target )
	{
		target = critterTarget;
	}

	if( target )
	{
		AttackReaction(target, 1, 0);
		if(target->IsPlayer())
		{
			WorldPacket data(SMSG_AI_REACTION, 12);
			data << m_Unit->GetGUID() << uint32(2);		// Aggro sound
			((Player*)target)->GetSession()->SendPacket(&data);
		}
		if(target->GetUInt32Value(UNIT_FIELD_CREATEDBY) != 0)
		{
			Unit* target2 = m_Unit->GetMapMgr()->GetPlayer(target->GetUInt32Value(UNIT_FIELD_CREATEDBY));
			/*if(!target2)
			{
				target2 = sObjHolder.GetObject<Player>(target->GetUInt32Value(UNIT_FIELD_CREATEDBY));
			}*/
			if(target2)
			{
				AttackReaction(target2, 1, 0);
			}
		}
	}
	return target;
}

Unit* AIInterface::FindTargetForSpell(AI_Spell *sp)
{
	/*if(!m_Unit) return NULL;*/

	/*if(!sp)
	{
		m_Unit->SetUInt64Value(UNIT_FIELD_TARGET, 0);
		return NULL;
	}*/

	TargetMap::iterator itr, itr2;

	if(sp)
	{
		if(sp->spellType == STYPE_HEAL)
		{
			uint32 cur = m_Unit->GetUInt32Value(UNIT_FIELD_HEALTH);
			uint32 max = m_Unit->GetUInt32Value(UNIT_FIELD_MAXHEALTH);
			float healthPercent = float(long2float(cur) / long2float(max));
			if(healthPercent <= sp->floatMisc1) // Heal ourselves cause we got too low HP
			{
				m_Unit->SetUInt64Value(UNIT_FIELD_TARGET, 0);
				return m_Unit;
			}
			for(AssistTargetSet::iterator i = m_assistTargets.begin(); i != m_assistTargets.end(); i++)
			{
				if(!(*i)->isAlive())
				{
					continue;
				}
				cur = (*i)->GetUInt32Value(UNIT_FIELD_HEALTH);
				max = (*i)->GetUInt32Value(UNIT_FIELD_MAXHEALTH);
				healthPercent = float(long2float(cur) / long2float(max));
				if(healthPercent <= sp->floatMisc1) // Heal ourselves cause we got too low HP
				{
					m_Unit->SetUInt64Value(UNIT_FIELD_TARGET, (*i)->GetGUID());
					return (*i); // heal Assist Target which has low HP
				}
			}
		}
		if(sp->spellType == STYPE_BUFF)
		{
			m_Unit->SetUInt64Value(UNIT_FIELD_TARGET, 0);
			return m_Unit;
		}
	}


	if(GetIsTaunted())
	{
		return tauntedBy;
	}
	else
	{
		return GetMostHated();
	}

	return NULL;
}

bool AIInterface::FindFriends(float dist)
{
	bool result = false;
	TargetMap::iterator it;

	std::set<Object*>::iterator itr;
	Unit *pUnit;

	

	for( itr = m_Unit->GetInRangeSetBegin(); itr != m_Unit->GetInRangeSetEnd(); itr++ )
	{
		if((*itr)->GetTypeId() != TYPEID_UNIT)
			continue;

		pUnit = static_cast<Unit*>((*itr));
		if(!pUnit->isAlive())
			continue;

		if(pUnit->HasFlag(UNIT_FIELD_FLAGS, U_FIELD_FLAG_UNIT_UNTACKABLE_NO_SELECT))
		{
			continue;
		}
		if(pUnit->HasFlag(UNIT_FIELD_FLAGS, U_FIELD_FLAG_UNIT_UNTACKABLE_SELECT) || pUnit->HasFlag(UNIT_FIELD_FLAGS, U_FIELD_FLAG_UNIT_UNTACKABLE_SELECT_2))
		{
			continue;
		}

		if(isCombatSupport(m_Unit, pUnit) 
			&& (pUnit->GetAIInterface()->getAIState() == STATE_IDLE || pUnit->GetAIInterface()->getAIState() == STATE_SCRIPTIDLE))//Not sure
		{
			if(m_Unit->GetDistanceSq(pUnit) < dist)
			{
				if(m_assistTargets.count(pUnit) > 0)	// already have him
					break;

				result = true;
				m_assistTargets.insert(pUnit);
					
				for(it = m_aiTargets.begin(); it != m_aiTargets.end(); ++it)
				{
					((Unit*)(*itr))->GetAIInterface()->AttackReaction(it->first, 1, 0);
				}
			}
		}
	}

	// check if we're a civillan, in which case summon guards on a despawn timer
	/*if(m_Unit->GetCreatureName() && m_Unit->GetCreatureName()->Civilian && getMSTime() > m_guardTimer && m_Unit->GetMapId() < 2)
	{
		switch(m_Unit->GetCreatureName()->Family)
		{
		case UNDEAD:
		case HUMANOID:
			break;
		default:
			return result;
			break;
		}

		m_Unit->SendChatMessage(CHAT_MSG_MONSTER_SAY, LANG_UNIVERSAL, "Guards, help me!");
		
		uint32 guardid = 1423;
		if(m_Unit->m_factionDBC->factionGroupReputationFrame == 67) // horde
			guardid = 3296; // ogri
		CreatureSpawnTemplate * pSpawnTemplate = objmgr.GetCreatureSpawnTemplate(guardid);
		if(pSpawnTemplate != 0)
		{
			Creature *pCreature = sObjHolder.CreateCreature();
			// FIXME: Summon hardcoded stormwind guard/ogrimmar grunt
			float x = m_Unit->GetPositionX() + (float)( (float)(rand() % 150 + 100) / 1000.0f );
			float y = m_Unit->GetPositionY() + (float)( (float)(rand() % 150 + 100) / 1000.0f );
			float z = m_Unit->GetPositionZ();
			float adt_z = m_Unit->GetMapMgr()->GetLandHeight(x, y);
			if(fabs(z - adt_z) < 3)
				z = adt_z;

			pCreature->Create(pSpawnTemplate, m_Unit->GetMapId(), x, y, z, 0);
			pCreature->SetInstanceID(m_Unit->GetInstanceID());
			pCreature->SetZoneId(m_Unit->GetZoneId());
			
			pCreature->AddToWorld();

			// set despawn timer
			// at 5mins
			sEventMgr.AddEvent(pCreature, &Creature::Despawn, EVENT_CREATURE_SAFE_DELETE, 60*5*1000, 1);

			for(it = m_aiTargets.begin(); it != m_aiTargets.end(); ++it)
			{
				pCreature->GetAIInterface()->AttackReaction(it->first, 1, 0);
			}
		}

		m_guardTimer = getMSTime() + 15000;
	}*/

	return result;
}

float AIInterface::_CalcAggroRange(Unit* target)
{
	//float baseAR = 15.0f; // Base Aggro Range
					// -8	 -7	 -6	 -5	 -4	 -3	 -2	 -1	 0	  +1	 +2	 +3	 +4	 +5	 +6	 +7	+8
	//float baseAR[17] = {29.0f, 27.5f, 26.0f, 24.5f, 23.0f, 21.5f, 20.0f, 18.5f, 17.0f, 15.5f, 14.0f, 12.5f, 11.0f,  9.5f,  8.0f,  6.5f, 5.0f};
	float baseAR[17] = {19.0f, 18.5f, 18.0f, 17.5f, 17.0f, 16.5f, 16.0f, 15.5f, 15.0f, 14.5f, 12.0f, 10.5f, 8.5f,  7.5f,  6.5f,  6.5f, 5.0f};
	// Lvl Diff -8 -7 -6 -5 -4 -3 -2 -1 +0 +1 +2  +3  +4  +5  +6  +7  +8
	// Arr Pos   0  1  2  3  4  5  6  7  8  9 10  11  12  13  14  15  16
	int8 lvlDiff = target->getLevel() - m_Unit->getLevel();
	uint8 realLvlDiff = lvlDiff;
	if(lvlDiff > 8)
	{
		lvlDiff = 8;
	}
	if(lvlDiff < -8)
	{
		lvlDiff = -8;
	}
	if(!((Creature*)m_Unit)->CanSee(target))
		return 0;
	
	float AggroRange = baseAR[lvlDiff + 8];
	if(realLvlDiff > 8)
	{
		AggroRange += AggroRange * ((lvlDiff - 8) * 5 / 100);
	}

	// Multiply by elite value
	if(((Creature*)m_Unit)->GetCreatureName() && ((Creature*)m_Unit)->GetCreatureName()->Rank > 0)
		AggroRange *= (((Creature*)m_Unit)->GetCreatureName()->Rank) * 1.50;

	if(AggroRange > 40.0f) // cap at 40.0f
	{
		AggroRange = 40.0f;
	}
  /*  //printf("aggro range: %f , stealthlvl: %d , detectlvl: %d\n",AggroRange,target->GetStealthLevel(),m_Unit->m_stealthDetectBonus);
	if(! ((Creature*)m_Unit)->CanSee(target))
	{
		AggroRange =0;
	//	AggroRange *= ( 100.0f - (target->m_stealthLevel - m_Unit->m_stealthDetectBonus)* 20.0f ) / 100.0f;
	}
*/
	// SPELL_AURA_MOD_DETECT_RANGE
	int32 modDetectRange = target->getDetectRangeMod(m_Unit->GetGUID());
	AggroRange += modDetectRange;
	if(target->IsPlayer())
		AggroRange += static_cast<Player*>(target)->DetectedRange;
	if(AggroRange < 3.0f)
	{
		AggroRange = 3.0f;
	}
	if(AggroRange > 40.0f) // cap at 40.0f
	{
		AggroRange = 40.0f;
	}

	return powf(AggroRange, 2);
}

void AIInterface::_CalcDestinationAndMove(Unit *target, float dist)
{
	if(!m_canMove || m_Unit->IsStunned())
	{
		StopMovement(0); //Just Stop
		return;
	}
	
	if(target->GetTypeId() == TYPEID_UNIT || target->GetTypeId() == TYPEID_PLAYER)
	{
		float angle = m_Unit->calcAngle(m_Unit->GetPositionX(), m_Unit->GetPositionY(), target->GetPositionX(), target->GetPositionY()) * M_PI / 180.0f;

		float x = dist * cosf(angle);
		float y = dist * sinf(angle);

		m_nextPosX = target->GetPositionX() - x;
		m_nextPosY = target->GetPositionY() - y;
		m_nextPosZ = target->GetPositionZ();
	}
	else
	{
		target = NULL;
		m_nextPosX = m_Unit->GetPositionX();
		m_nextPosY = m_Unit->GetPositionY();
		m_nextPosZ = m_Unit->GetPositionZ();
	}

	float dx = m_nextPosX - m_Unit->GetPositionX();
	float dy = m_nextPosY - m_Unit->GetPositionY();
	if(dy != 0.0f)
	{
		float angle = atan2(dx, dy);
		m_Unit->SetOrientation(angle);
	}

	if(m_creatureState != MOVING)
		UpdateMove();
}

float AIInterface::_CalcCombatRange(Unit* target, bool ranged)
{
	if(!target)
	{
		return 0;
	}
	float range = 0.0f;
	float rang = 1.5f;
	if(ranged)
	{
		rang = 5.0f;
	}

	float selfreach = m_Unit->GetFloatValue(UNIT_FIELD_COMBATREACH);
	float targetradius = target->GetFloatValue(UNIT_FIELD_BOUNDINGRADIUS);
	float selfradius = m_Unit->GetFloatValue(UNIT_FIELD_BOUNDINGRADIUS);
	float targetscale = target->GetFloatValue(OBJECT_FIELD_SCALE_X);
	float selfscale = m_Unit->GetFloatValue(OBJECT_FIELD_SCALE_X);

	range = (((powf(targetradius,2)*targetscale) + selfreach) + ((selfradius*selfscale) + rang));
	if(range > 28.29f) range = 28.29f;
	return range;
}

float AIInterface::_CalcDistanceFromHome()
{
	if (m_AIType == AITYPE_PET)
	{
		return m_Unit->GetDistanceSq(m_PetOwner);
	}
	else if(m_Unit->GetTypeId() == TYPEID_UNIT)
	{

		if(m_returnX !=0.0f && m_returnY != 0.0f)
		{
			return m_Unit->GetDistanceSq(m_returnX,m_returnY,m_returnZ);
		}
	}

	return 0.0f;
}

void AIInterface::SendMoveToPacket(float toX, float toY, float toZ, float toO, uint32 time, uint32 MoveFlags)
{
	//this should NEVER be called directly !!!!!!
	//use MoveTo()

	/*
	Move Flags
	0x00000000 - Walk
	0x00000100 - Run
	0x00000300 - Fly
	*/
	StackWorldPacket<60> data(SMSG_MONSTER_MOVE);
	data << m_Unit->GetNewGUID();
	data << m_Unit->GetPositionX() << m_Unit->GetPositionY() << m_Unit->GetPositionZ();
	data << getMSTime();
	
	// Check if we have an orientation
	if(toO != 0.0f)
	{
		data << uint8(4);
		data << toO;
	} else {
		data << uint8(0);
	}
	data << MoveFlags;
	data << time;
	data << uint32(1);	  // 1 waypoint
	data << toX << toY << toZ;

	if(m_Unit->GetTypeId() == TYPEID_PLAYER)
		m_Unit->SendMessageToSet( &data, true );
	else
		m_Unit->SendMessageToSet( &data, false );
}

/*
void AIInterface::SendMoveToSplinesPacket(std::list<Waypoint> wp, bool run)
{
	if(!m_canMove)
	{
		return;
	}

	WorldPacket data;

	uint8 DontMove = 0;
	uint32 travelTime = 0;
	for(std::list<Waypoint>::iterator i = wp.begin(); i != wp.end(); i++)
	{
		travelTime += i->time;
	}

	data.Initialize( SMSG_MONSTER_MOVE );
	data << m_Unit->GetNewGUID();
	data << m_Unit->GetPositionX() << m_Unit->GetPositionY() << m_Unit->GetPositionZ();
	data << getMSTime();
	data << uint8(DontMove);
	data << uint32(run ? 0x00000100 : 0x00000000);
	data << travelTime;
	data << (uint32)wp.size();
	for(std::list<Waypoint>::iterator i = wp.begin(); i != wp.end(); i++)
	{
		data << i->x;
		data << i->y;
		data << i->z;
	}

	m_Unit->SendMessageToSet( &data, false );
}
*/
bool AIInterface::StopMovement(uint32 time)
{
	m_moveTimer = time; //set pause after stopping
	m_creatureState = STOPPED;

	m_destinationX = m_destinationY = m_destinationZ = 0;
	m_nextPosX = m_nextPosY = m_nextPosZ = 0;
	m_timeMoved = 0;
	m_timeToMove = 0;

	WorldPacket data(26);
	data.SetOpcode(SMSG_MONSTER_MOVE);
	data << m_Unit->GetNewGUID();
	data << m_Unit->GetPositionX() << m_Unit->GetPositionY() << m_Unit->GetPositionZ();
	data << getMSTime();
	data << uint8(1);   // "DontMove = 1"

	m_Unit->SendMessageToSet( &data, false );
	return true;
}

void AIInterface::MoveTo(float x, float y, float z, float o)
{
	m_sourceX = m_Unit->GetPositionX();
	m_sourceY = m_Unit->GetPositionY();
	m_sourceZ = m_Unit->GetPositionZ();

	if(!m_canMove || m_Unit->IsStunned())
	{
		StopMovement(0); //Just Stop
		return;
	}

	m_nextPosX = x;
	m_nextPosY = y;
	m_nextPosZ = z;

	if(m_creatureState != MOVING)
		UpdateMove();
}

bool AIInterface::IsFlying()
{
	if(m_moveFly)
		return true;
	
	/*float z = m_Unit->GetMapMgr()->GetLandHeight(m_Unit->GetPositionX(), m_Unit->GetPositionY());
	if(z)
	{
		if(m_Unit->GetPositionZ() >= (z + 1.0f)) //not on ground? Oo
		{
			return true;
		}
	}
	return false;*/
	if(m_Unit->GetTypeId() == TYPEID_PLAYER)
		return ((Player*)m_Unit)->FlyCheat;

	return false;
}

uint32 AIInterface::getMoveFlags()
{
	uint32 MoveFlags = 0;
	if(m_moveFly == true) //Fly
	{
		m_moveSpeed = m_Unit->m_flySpeed*0.001f;
		MoveFlags = 0x300;
	}
	else if(m_moveSprint == true) //Sprint
	{
		m_moveSpeed = (m_Unit->m_runSpeed+5.0f)*0.001f;
		MoveFlags = 0x100;
	}
	else if(m_moveRun == true) //Run
	{
		m_moveSpeed = m_Unit->m_runSpeed*0.001f;
		MoveFlags = 0x100;
	}
	else //Walk
	{
		m_moveSpeed = m_Unit->m_walkSpeed*0.001f;
		MoveFlags = 0x000;
	}
	return MoveFlags;
}

void AIInterface::UpdateMove()
{
	//this should NEVER be called directly !!!!!!
	//use MoveTo()
	float distance = m_Unit->CalcDistance(m_nextPosX,m_nextPosY,m_nextPosZ);
	
	if(distance < 1.0f) return; //we don't want little movements here and there

	m_destinationX = m_nextPosX;
	m_destinationY = m_nextPosY;
	m_destinationZ = m_nextPosZ;
	
	if(m_moveFly != true)
	{
		if(m_Unit->GetMapMgr())
		{
			float adt_Z = m_Unit->GetMapMgr()->GetLandHeight(m_destinationX, m_destinationY);
			if(fabsf(adt_Z - m_destinationZ) < 1.5)
				m_destinationZ = adt_Z;
		}
	}
	m_nextPosX = m_nextPosY = m_nextPosZ = 0;

	uint32 moveTime = (uint32) (distance / m_moveSpeed);
	m_totalMoveTime = moveTime;

	if(m_Unit->GetTypeId() == TYPEID_UNIT)
	{
		Creature *creature = static_cast<Creature*>(m_Unit);
		// check if we're returning to our respawn location. if so, reset back to default
		// orientation
		if(creature->GetSpawnX() == m_destinationX &&
			creature->GetSpawnY() == m_destinationY)
		{
			float o = creature->GetSpawnO();
			creature->SetOrientation(o);
		} else {
			// Calculate the angle to our next position

			float dx = (float)m_destinationX - m_Unit->GetPositionX();
			float dy = (float)m_destinationY - m_Unit->GetPositionY();
			if(dy != 0.0f)
			{
				float angle = atan2(dy, dx);
				m_Unit->SetOrientation(angle);
			}
		}
	}
	SendMoveToPacket(m_destinationX, m_destinationY, m_destinationZ, m_Unit->GetOrientation(), moveTime, getMoveFlags());

	m_timeToMove = moveTime;
	m_timeMoved = 0;
	if(m_moveTimer == 0)
	{
		m_moveTimer =  UNIT_MOVEMENT_INTERPOLATE_INTERVAL; // update every few msecs
	}

	m_creatureState = MOVING;
}

void AIInterface::SendCurrentMove(Player* plyr/*uint64 guid*/)
{
	if(m_destinationX == 0.0f && m_destinationY == 0.0f && m_destinationZ == 0.0f) return; //invalid move 
	ByteBuffer *splineBuf = new ByteBuffer(20*4);
	*splineBuf << uint32(0); // spline flags
	*splineBuf << uint32((m_totalMoveTime - m_timeToMove)+m_moveTimer); //Time Passed (start Position) //should be generated/save 
	*splineBuf << uint32(m_totalMoveTime); //Total Time //should be generated/save
	*splineBuf << uint32(0); //Unknown
	*splineBuf << uint32(4); //Spline Count	// lets try this

	*splineBuf << m_sourceX << m_sourceY << m_sourceZ;
	*splineBuf << m_Unit->GetPositionX() << m_Unit->GetPositionY() << m_Unit->GetPositionZ();
	*splineBuf << m_destinationX << m_destinationY << m_destinationZ;
	*splineBuf << m_destinationX << m_destinationY << m_destinationZ;
	*splineBuf << m_destinationX << m_destinationY << m_destinationZ;

	plyr->AddSplinePacket(m_Unit->GetGUID(), splineBuf);

	//This should only be called by Players AddInRangeObject() ONLY
	//using guid cuz when i atempted to use pointer the player was deleted when this event was called some times
	//Player* plyr = World::GetPlayer(guid);
	//if(!plyr) return;

	/*if(m_destinationX == 0.0f && m_destinationY == 0.0f && m_destinationZ == 0.0f) return; //invalid move 
	uint32 moveTime = m_timeToMove-m_timeMoved;
	//uint32 moveTime = (m_timeToMove-m_timeMoved)+m_moveTimer;
	WorldPacket data(50);
	data.SetOpcode( SMSG_MONSTER_MOVE );
	data << m_Unit->GetNewGUID();
	data << m_Unit->GetPositionX() << m_Unit->GetPositionY() << m_Unit->GetPositionZ();
	data << getMSTime();
	data << uint8(0);
	data << getMoveFlags();

	//float distance = m_Unit->CalcDistance(m_destinationX, m_destinationY, m_destinationZ);
	//uint32 moveTime = (uint32) (distance / m_moveSpeed);

	data << moveTime;
	data << uint32(1); //Number of Waypoints
	data << m_destinationX << m_destinationY << m_destinationZ;
	plyr->GetSession()->SendPacket(&data);*/

}

bool AIInterface::setInFront(Unit* target) // not the best way to do it, though
{
	//angle the object has to face
	float angle = m_Unit->calcAngle(m_Unit->GetPositionX(), m_Unit->GetPositionY(), target->GetPositionX(), target->GetPositionY() ); 
	//Change angle slowly 2000ms to turn 180 deg around
	if(angle > 180) angle += 90;
	else angle -= 90; //angle < 180
	m_Unit->getEasyAngle(angle);
	//Convert from degrees to radians (180 deg = PI rad)
	float orientation = angle / float(180 / M_PI);
	//Update Orentation Server Side
	m_Unit->SetPosition(m_Unit->GetPositionX(), m_Unit->GetPositionY(), m_Unit->GetPositionZ(), orientation);
	
	return m_Unit->isInFront(target);
}

bool AIInterface::addWayPoint(WayPoint* wp)
{
	if(!m_waypoints)
		m_waypoints = new WayPointMap ;
	if(!wp) 
		return false;
	if(wp->id <= 0)
		return false; //not valid id

	if(m_waypoints->find( wp->id ) == m_waypoints->end( ))
	{
		//m_waypoints[wp->id] = wp;
		m_waypoints->insert(WayPointMap::value_type(wp->id, wp));
		return true;
	}
	return false;
}

void AIInterface::changeWayPointID(uint32 oldwpid, uint32 newwpid)
{
	if(!m_waypoints)return;
	if(newwpid <= 0) 
		return; //not valid id
	if(newwpid > m_waypoints->size()) 
		return; //not valid id
	if(newwpid == oldwpid) 
		return; //same spot

	//already wp with that id ?
	WayPoint* originalwp = getWayPoint(newwpid);
	if(!originalwp) 
		return;
	WayPoint* oldwp = getWayPoint(oldwpid);
	if(!oldwp) 
		return;

	//we have valid wps in the positions
	WayPoint* wpnext = NULL;
	WayPoint* wp = NULL;
	uint32 i = 0;
	uint32 endat = 0;
	//reorder freeing newwpid's spot
	if((oldwpid+1 == newwpid) || (newwpid+1 == oldwpid))
	{
		//within 1 place of eachother just swap
		oldwp->id = newwpid;
		originalwp->id = oldwpid;
		(*m_waypoints)[newwpid] = oldwp;
		(*m_waypoints)[oldwpid] = originalwp;
	}
	//		4		  2
	else if(oldwpid > newwpid) // move others up
	{
		//2	   4		 2
		endat = (oldwpid-newwpid);

		for(i = 0; i < endat;i++)
		{
			//				  2
			wpnext = getWayPoint(newwpid+i+1); //waypoint 3

			if(i == 0)
			{
				//First move
				oldwp->id = newwpid;
				(*m_waypoints)[newwpid] = oldwp; //position 2

				originalwp->id = newwpid+1;
				(*m_waypoints)[newwpid+1] = originalwp; //position 3
			}
			else //i > 0
			{
				if(wp)
				{
					wp->id = newwpid+1+i;
					(*m_waypoints)[newwpid+1+i] = wp;
				}
			}
			wp = wpnext;
		}
	}
	//		2		  4
	else if(oldwpid < newwpid) //move others down
	{
		//2	   4		 2
		endat = (newwpid-oldwpid);

		for(i = 0; i < endat;i++)
		{
			//				  2
			wp = getWayPoint(oldwpid+i+1); //waypoint 3
			if(wp)
			{
				wp->id = oldwpid+i;
				(*m_waypoints)[oldwpid+i] = wp; //position 2
			}
			if(i == endat-1)
			{
				oldwp->id = newwpid;
				(*m_waypoints)[newwpid] = oldwp; //position 4
			}
		}
	}

	//SaveAll to db
	saveWayPoints(0);
}

void AIInterface::deleteWayPoint(uint32 wpid, bool save)
{
	if(!m_waypoints)return;
	if(wpid <= 0) 
		return; //not valid id
	if(wpid > m_waypoints->size()) 
		return; //not valid id

	uint32 startsize = m_waypoints->size();
	
	//Delete Waypoint
	WayPointMap::iterator iter = m_waypoints->find( wpid );
	if( iter != m_waypoints->end() )
	{
		delete iter->second;
		m_waypoints->erase( iter );
	}
	
	WayPoint* wp = NULL;
	//Reorginise the rest
	if(wpid <= m_waypoints->size()) //non existant wp
	{
		uint32 i = 0;
		for(i = 0; i < startsize-wpid;i++)
		{
			wp = (*m_waypoints)[wpid+i+1];
			if(wp)
			{
				wp->id = wpid+i;
				(*m_waypoints)[wpid+i] = wp;
			}
		}
		m_waypoints->erase( wpid+i );
	}
	if(m_waypoints->size()==0)
	{
		delete m_waypoints;
		m_waypoints = NULL;
	}

	//SaveAll to db
	if(save)
		saveWayPoints(0);
}

bool AIInterface::showWayPoints(uint32 wpid, Player* pPlayer, bool Backwards)
{
	if(!m_waypoints)
		return false;

	if(wpid > m_waypoints->size()) 
		return false; //not valid id

	//wpid of 0 == all
	WayPointMap::const_iterator itr;
	if((m_WayPointsShowing == true) && (wpid == 0)) 
		return false;
	m_WayPointsShowing = true;

	WayPoint* wp = NULL;
	for (itr = m_waypoints->begin(); itr != m_waypoints->end(); itr++)
	{
		if( (itr->second->id == wpid) || (wpid == 0) )
		{
			wp = itr->second;

			//Create
			Creature* pWayPoint = new Creature(HIGHGUID_WAYPOINT ,wp->id);
			pWayPoint->CreateWayPoint(wp->id,pPlayer->GetMapId(),wp->x,wp->y,wp->z,0);
			pWayPoint->SetUInt32Value(OBJECT_FIELD_ENTRY, 300000);
			pWayPoint->SetFloatValue(OBJECT_FIELD_SCALE_X, 0.5f);
			if(Backwards)
			{
				uint32 DisplayID = (wp->backwardskinid == 0)? GetUnit()->GetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID) : wp->backwardskinid;
				pWayPoint->SetUInt32Value(UNIT_FIELD_DISPLAYID, DisplayID);
				pWayPoint->SetUInt32Value(UNIT_NPC_EMOTESTATE, wp->backwardemoteid);
			}
			else
			{
				uint32 DisplayID = (wp->forwardskinid == 0)? GetUnit()->GetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID) : wp->forwardskinid;
				pWayPoint->SetUInt32Value(UNIT_FIELD_DISPLAYID, DisplayID);
				pWayPoint->SetUInt32Value(UNIT_NPC_EMOTESTATE, wp->forwardemoteid);
			}
			pWayPoint->SetUInt32Value(UNIT_FIELD_LEVEL, wp->id);
			pWayPoint->SetUInt32Value(UNIT_NPC_FLAGS, 0);
			pWayPoint->SetUInt32Value(UNIT_FIELD_AURA+32, 8326); //invisable & deathworld look
			pWayPoint->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE , pPlayer->GetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE));
			pWayPoint->SetUInt32Value(UNIT_FIELD_HEALTH, 1);
			pWayPoint->SetUInt32Value(UNIT_FIELD_MAXHEALTH, 1);

			//Create on client
			ByteBuffer buf(2500);
			uint32 count = pWayPoint->BuildCreateUpdateBlockForPlayer(&buf, pPlayer);
			pPlayer->PushUpdateData(&buf, count);

			//root the object
			WorldPacket data1;
			data1.Initialize(SMSG_FORCE_MOVE_ROOT);
			data1 << pWayPoint->GetNewGUID();
			pPlayer->GetSession()->SendPacket( &data1 );

			//Cleanup
			//delete data;
			delete pWayPoint;
			if(wpid != 0) return true;
		}
	}
	return true;
}

bool AIInterface::hideWayPoints(uint32 wpid, Player* pPlayer)
{
	if(!m_waypoints)
		return false;

	if(wpid > m_waypoints->size()) return false; //not valid id

	//wpid of 0 == all
	if(m_WayPointsShowing != true) return false;
	m_WayPointsShowing = false;
	WayPointMap::const_iterator itr;

	// slightly better way to do this
	uint64 guid;

	for (itr = m_waypoints->begin(); itr != m_waypoints->end(); itr++)
	{
		if( (itr->second->id == wpid) || (wpid == 0) )
		{
			// avoid C4293
			guid = ((uint64)HIGHGUID_WAYPOINT << 32) | itr->second->id;
			WoWGuid wowguid(guid);
			pPlayer->PushOutOfRange(wowguid);
			if(wpid != 0) return true;
		}
	}
	return true;
}

bool AIInterface::saveWayPoints(uint32 wpid)
{
	if(!m_waypoints)return false;

	if(wpid > m_waypoints->size()) return false; //not valid id
	if(!GetUnit()) return false;
	if(GetUnit()->GetTypeId() != TYPEID_UNIT) return false;

	//wpid of 0 == all
	std::stringstream ss;
	if(wpid == 0)
	{
		//Delete
		ss << "DELETE FROM creature_waypoints WHERE creatureid = " << ((Creature*)GetUnit())->GetSQL_id() << "\0";
		WorldDatabase.Query( ss.str().c_str() );
	}

	WayPointMap::const_iterator itr;

	WayPoint* wp = NULL;
	for (itr = m_waypoints->begin(); itr != m_waypoints->end(); itr++)
	{
		if(itr == m_waypoints->end()) 
			break;
		if(!itr->second) 
			continue;

		if( (itr->second->id == wpid) || (wpid == 0) )
		{
			wp = itr->second;

			if(wpid != 0)
			{
				//Delete
				ss.str("");
				ss << "DELETE FROM creature_waypoints WHERE creatureid = " << ((Creature*)GetUnit())->GetSQL_id() << " and waypointid = " << wp->id << "\0";
				WorldDatabase.Query( ss.str().c_str() );
			}

			//Save
			ss.str("");
			ss << "INSERT INTO creature_waypoints ";
			ss << "(creatureid,waypointid,x,y,z,waittime,flags,forwardemoteoneshot,forwardemoteid,backwardemoteoneshot,backwardemoteid,forwardskinid,backwardskinid) VALUES (";
			ss << ((Creature*)GetUnit())->GetSQL_id() << ", ";
			ss << wp->id << ", ";
			ss << wp->x << ", ";
			ss << wp->y << ", ";
			ss << wp->z << ", ";
			ss << wp->waittime << ", ";
			ss << wp->flags << ", ";
			ss << wp->forwardemoteoneshot << ", ";
			ss << wp->forwardemoteid << ", ";
			ss << wp->backwardemoteoneshot << ", ";
			ss << wp->backwardemoteid << ", ";
			ss << wp->forwardskinid << ", ";
			ss << wp->backwardskinid << ")\0";
			WorldDatabase.Query( ss.str().c_str() );

			if(wpid != 0) 
				return true;
		}
	}
	return false;
}

WayPoint* AIInterface::getWayPoint(uint32 wpid)
{
	if(!m_waypoints)return NULL;
	if(wpid > m_waypoints->size()) 
		return NULL; //not valid id

	WayPointMap::const_iterator itr = m_waypoints->find( wpid );
	if( itr != m_waypoints->end( ) )
		return itr->second;
	return NULL;
}

void AIInterface::_UpdateMovement(uint32 p_time)
{
	if(!m_Unit->isAlive())
	{
		StopMovement(0);
		return;
	}

	uint32 timediff = 0;

	if(m_moveTimer > 0)
	{
		if(p_time >= m_moveTimer)
		{
			timediff = p_time - m_moveTimer;
			m_moveTimer = 0;
		}
		else
			m_moveTimer -= p_time;
	}

	if(m_timeToMove > 0)
	{
		m_timeMoved = m_timeToMove <= p_time + m_timeMoved ? m_timeToMove : p_time + m_timeMoved;
	}

	if(m_creatureState == MOVING)
	{
		if(!m_moveTimer)
		{
			if(m_timeMoved == m_timeToMove) //reached destination
			{
				if(m_moveType == MOVEMENTTYPE_WANTEDWP)//We reached wanted wp stop now
					m_moveType = MOVEMENTTYPE_DONTMOVEWP;

				float wayO = 0.0f;

				if((GetWayPointsCount() != 0) && (m_AIState == STATE_IDLE || m_AIState == STATE_SCRIPTMOVE)) //if we attacking don't use wps
				{
					WayPoint* wp = getWayPoint(getCurrentWaypoint());
					if(wp)
					{
						CALL_SCRIPT_EVENT(m_Unit, OnReachWP)(wp->id, !m_moveBackward);
						ScriptSystem->OnCreatureEventArg(((Creature*)m_Unit), wp->id, CREATURE_EVENT_ON_REACH_WP);

						if(((Creature*)m_Unit)->has_waypoint_text)
							objmgr.HandleMonsterSayEvent(((Creature*)m_Unit), MONSTER_SAY_EVENT_RANDOM_WAYPOINT);

						//Lets face to correct orientation
						wayO = wp->o;
						m_moveTimer = wp->waittime; //wait before next move
						if(!m_moveBackward)
						{
							if(wp->forwardemoteoneshot)
							{
								GetUnit()->Emote(EmoteType(wp->forwardemoteid));
							}
							else
							{
								if(GetUnit()->GetUInt32Value(UNIT_NPC_EMOTESTATE) != wp->forwardemoteid)
								{
									GetUnit()->SetUInt32Value(UNIT_NPC_EMOTESTATE, wp->forwardemoteid);
								}
							}
						}
						else
						{
							if(wp->backwardemoteoneshot)
							{
								GetUnit()->Emote(EmoteType(wp->backwardemoteid));
							}
							else
							{
								if(GetUnit()->GetUInt32Value(UNIT_NPC_EMOTESTATE) != wp->backwardemoteid)
								{
									GetUnit()->SetUInt32Value(UNIT_NPC_EMOTESTATE, wp->backwardemoteid);
								}
							}
						}
					}
					else
						m_moveTimer = sRand.randInt(m_moveRun ? 5000 : 10000); // wait before next move
				}

				m_creatureState = STOPPED;
				m_moveSprint = false;

				if(m_MovementType == MOVEMENTTYPE_DONTMOVEWP)
					m_Unit->SetPosition(m_destinationX, m_destinationY, m_destinationZ, wayO, true);
				else
					m_Unit->SetPosition(m_destinationX, m_destinationY, m_destinationZ, m_Unit->GetOrientation(), true);

				m_destinationX = m_destinationY = m_destinationZ = 0;
				m_timeMoved = 0;
				m_timeToMove = 0;
			}
			else
			{
				//Move Server Side Update
				float q = (float)m_timeMoved / (float)m_timeToMove;
				float x = m_Unit->GetPositionX() + (m_destinationX - m_Unit->GetPositionX()) * q;
				float y = m_Unit->GetPositionY() + (m_destinationY - m_Unit->GetPositionY()) * q;
				float z = m_Unit->GetPositionZ() + (m_destinationZ - m_Unit->GetPositionZ()) * q;
				
				if(m_moveFly != true)
				{
					if(m_Unit->GetMapMgr())
					{
						float adt_Z = m_Unit->GetMapMgr()->GetLandHeight(x, y);
						if(fabsf(adt_Z - z) < 1.5)
							z = adt_Z;
					}
				}

				m_Unit->SetPosition(x, y, z, m_Unit->GetOrientation());
				
				m_timeToMove -= m_timeMoved;
				m_timeMoved = 0;
				m_moveTimer = (UNIT_MOVEMENT_INTERPOLATE_INTERVAL < m_timeToMove) ? UNIT_MOVEMENT_INTERPOLATE_INTERVAL : m_timeToMove;
			}
			//**** Movement related stuff that should be done after a move update (Keeps Client and Server Synced) ****//
			//**** Process the Pending Move ****//
			if(m_nextPosX != 0.0f && m_nextPosY != 0.0f)
			{
				UpdateMove();
			}
		}
	}
	else if(m_creatureState == STOPPED && (m_AIState == STATE_IDLE || m_AIState == STATE_SCRIPTMOVE) && !m_moveTimer && !m_timeToMove && UnitToFollow == NULL) //creature is stopped and out of Combat
	{
		if(sWorld.getAllowMovement() == false) //is creature movement enabled?
			return;

		if(m_Unit->GetUInt32Value(UNIT_FIELD_DISPLAYID) == 5233) //if Spirit Healer don't move
			return;

		// do we have a formation?
		if(m_formationLinkSqlId != 0)
		{
			if(!m_formationLinkTarget)
			{
				// haven't found our target yet
				Creature * c = static_cast<Creature*>(m_Unit);
				if(!c->haslinkupevent)
				{
					// register linkup event
					c->haslinkupevent = true;
					sEventMgr.AddEvent(c, &Creature::FormationLinkUp, m_formationLinkSqlId, 
						EVENT_CREATURE_FORMATION_LINKUP, 1000, 0);
				}
			}
			else
			{
				// we've got a formation target, set unittofollow to this
				UnitToFollow = m_formationLinkTarget;
				FollowDistance = m_formationFollowDistance;
				m_fallowAngle = m_formationFollowAngle;
			}
		}
		if(UnitToFollow == 0)
		{
			// no formation, use waypoints
			int destpoint = -1;

			// If creature has no waypoints just wander aimlessly around spawnpoint
			if(GetWayPointsCount()==0) //no waypoints
			{
				/*	if(m_moveRandom)
				{
				if((rand()%10)==0)																																	
				{																																								  
				float wanderDistance = rand()%4 + 2;
				float wanderX = ((wanderDistance*rand()) / RAND_MAX) - wanderDistance / 2;																											   
				float wanderY = ((wanderDistance*rand()) / RAND_MAX) - wanderDistance / 2;																											   
				float wanderZ = 0; // FIX ME ( i dont know how to get apropriate Z coord, maybe use client height map data)																											 

				if(m_Unit->CalcDistance(m_Unit->GetPositionX(), m_Unit->GetPositionY(), m_Unit->GetPositionZ(), ((Creature*)m_Unit)->respawn_cord[0], ((Creature*)m_Unit)->respawn_cord[1], ((Creature*)m_Unit)->respawn_cord[2])>15)																		   
				{   
				//return home																																				 
				MoveTo(((Creature*)m_Unit)->respawn_cord[0],((Creature*)m_Unit)->respawn_cord[1],((Creature*)m_Unit)->respawn_cord[2],false);
				}   
				else
				{
				MoveTo(m_Unit->GetPositionX() + wanderX, m_Unit->GetPositionY() + wanderY, m_Unit->GetPositionZ() + wanderZ,false);
				}	
				}	
				}
				*/	
				return;																																				   
			}																																						  
			else //we do have waypoints
			{
				if(m_moveType == MOVEMENTTYPE_RANDOMWP) //is random move on if so move to a random waypoint
				{
					if(GetWayPointsCount() > 1)
						destpoint = sRand.randInt(GetWayPointsCount());
				}
				else if (m_moveType == MOVEMENTTYPE_CIRCLEWP) //random move is not on lets follow the path in circles
				{
					// 1 -> 10 then 1 -> 10
					m_currentWaypoint++;
					if (m_currentWaypoint > GetWayPointsCount()) m_currentWaypoint = 1; //Happens when you delete last wp seems to continue ticking
					destpoint = m_currentWaypoint;
					m_moveBackward = false;
				}
				else if(m_moveType == MOVEMENTTYPE_WANTEDWP)//Move to wanted wp
				{
					if(m_currentWaypoint)
					{
						if(GetWayPointsCount() > 0)
						{
							destpoint = m_currentWaypoint;
						}
						else
							destpoint = -1;
					}
				}
				else if(m_moveType == MOVEMENTTYPE_FORWARDTHANSTOP)// move to end, then stop
				{
					++m_currentWaypoint;
					if(m_currentWaypoint > GetWayPointsCount())
						destpoint = -1;
					else
						destpoint = m_currentWaypoint;
				}
				else if(m_moveType != MOVEMENTTYPE_QUEST && m_moveType != MOVEMENTTYPE_DONTMOVEWP)//4 Unused
				{
					// 1 -> 10 then 10 -> 1
					if (m_currentWaypoint > GetWayPointsCount()) m_currentWaypoint = 1; //Happens when you delete last wp seems to continue ticking
					if (m_currentWaypoint == GetWayPointsCount()) // Are we on the last waypoint? if so walk back
						m_moveBackward = true;
					if (m_currentWaypoint == 1) // Are we on the first waypoint? if so lets goto the second waypoint
						m_moveBackward = false;
					if (!m_moveBackward) // going 1..n
						destpoint = ++m_currentWaypoint;
					else				// going n..1
						destpoint = --m_currentWaypoint;
				}

				if(destpoint != -1)
				{
					WayPoint* wp = getWayPoint(destpoint);
					if(wp)
					{
						if(!m_moveBackward)
						{
							if((wp->forwardskinid != 0) && (GetUnit()->GetUInt32Value(UNIT_FIELD_DISPLAYID) != wp->forwardskinid))
							{
								GetUnit()->SetUInt32Value(UNIT_FIELD_DISPLAYID, wp->forwardskinid);
							}
						}
						else
						{
							if((wp->backwardskinid != 0) && (GetUnit()->GetUInt32Value(UNIT_FIELD_DISPLAYID) != wp->backwardskinid))
							{
								GetUnit()->SetUInt32Value(UNIT_FIELD_DISPLAYID, wp->backwardskinid);
							}
						}
						m_moveFly = (wp->flags == 768) ? 1 : 0;
						m_moveRun = (wp->flags == 256) ? 1 : 0;
						MoveTo(wp->x, wp->y, wp->z, 0);
					}
				}
			}
		}
	}

	//Fear Code
	if(m_AIState == STATE_FEAR && UnitToFear != NULL && m_creatureState == STOPPED)
	{
		if(getMSTime() > m_FearTimer)   // Wait at point for x ms ;)
		{
			float Fx;
			float Fy;
			float Fz;
			// Calculate new angle to target.
			float Fo = m_Unit->calcRadAngle(UnitToFear->GetPositionX(), UnitToFear->GetPositionY(), m_Unit->GetPositionX(), m_Unit->GetPositionY());
				
			Fx = m_Unit->GetPositionX() + 10*cosf(Fo);
			Fy = m_Unit->GetPositionY() + 10*sinf(Fo);
	
			// Check if this point is in water.
			float wl = m_Unit->GetMapMgr()->GetWaterHeight(Fx, Fy);
//			uint8 wt = m_Unit->GetMapMgr()->GetWaterType(Fx, Fy);
	
			Fz = m_Unit->GetMapMgr()->GetLandHeight(Fx, Fy);
			if(!(fabs(m_Unit->GetPositionZ() - Fz) > 4 || Fz < (wl-2))/* && wt & 0x1*/)
			{		
				MoveTo(Fx, Fy, Fz, Fo);
			}
			m_FearTimer = m_totalMoveTime + getMSTime() + 200;
		}
	}
	
	if(m_AIState == STATE_WANDER && m_creatureState == STOPPED)
	{
		float wanderO = sRand.randInt(6);
		float wanderX = m_Unit->GetPositionX() + cosf(wanderO);																											 
		float wanderY = m_Unit->GetPositionY() + sinf(wanderO);																											  
		float wanderZ;
			


		// Check if this point is in water.
		float wl = m_Unit->GetMapMgr()->GetWaterHeight(wanderX, wanderY);
//		uint8 wt = m_Unit->GetMapMgr()->GetWaterType(wanderX, wanderY);

		wanderZ = m_Unit->GetMapMgr()->GetLandHeight(wanderX, wanderY);
		if(!(fabs(m_Unit->GetPositionZ() - wanderZ) > 4 || wanderZ < (wl-2))/* && wt & 0x1*/)
		{
			MoveTo(wanderX,wanderY,wanderZ,wanderO);
		}   
	}

	//Unit Follow Code
	if(UnitToFollow != NULL && (m_AIState == STATE_IDLE || m_AIState == STATE_FOLLOWING) && UnitToFollow->IsInWorld())
	{
		float dist = m_Unit->GetDistanceSq(UnitToFollow);

		// re-calculate orientation based on target's movement
		if(m_lastFollowX != UnitToFollow->GetPositionX() ||
			m_lastFollowY != UnitToFollow->GetPositionY())
		{
			float dx = UnitToFollow->GetPositionX() - m_Unit->GetPositionX();
			float dy = UnitToFollow->GetPositionY() - m_Unit->GetPositionY();
			if(dy != 0.0f)
			{
				float angle = atan2(dx,dy);
				m_Unit->SetOrientation(angle);
			}
			m_lastFollowX = UnitToFollow->GetPositionX();
			m_lastFollowY = UnitToFollow->GetPositionY();
		}

		if (dist > powf(FollowDistance,2)) //if out of range
		{
			m_AIState = STATE_FOLLOWING;
			
			if(dist > 25.0f) //25 yard away lets run else we will loose the them
				m_moveRun = true;
			else 
				m_moveRun = false;

			if(m_AIType == AITYPE_PET || UnitToFollow == m_formationLinkTarget) //Unit is Pet/formation
			{
				if(dist > 900.0f/*30*/)
					m_moveSprint = true;

				// estimate position based on orientation
				/*float delta_x = -(sinf(UnitToFollow->GetOrientation()) * 5);
				float delta_y = -(cosf(UnitToFollow->GetOrientation()) * 5);

				delta_x += UnitToFollow->GetPositionX();
				delta_y += UnitToFollow->GetPositionY();*/
				float delta_x = UnitToFollow->GetPositionX();
				float delta_y = UnitToFollow->GetPositionY();
				float d = 3;
				if(m_formationLinkTarget)
					d = m_formationFollowDistance;

				MoveTo(delta_x+(d*(cosf(m_fallowAngle+UnitToFollow->GetOrientation()))),
					   delta_y+(d*(sinf(m_fallowAngle+UnitToFollow->GetOrientation()))),
					   UnitToFollow->GetPositionZ(),UnitToFollow->GetOrientation());
			}
			else
			{
				_CalcDestinationAndMove(UnitToFollow, FollowDistance);
			}
		}
	}
}

void AIInterface::CastSpell(Unit* caster, SpellEntry *spellInfo, SpellCastTargets targets)
{
	// Stop movement while casting.
	m_AIState = STATE_CASTING;
#ifdef _AI_DEBUG
	sLog.outString("AI DEBUG: Unit %u casting spell %s on target "I64FMT, caster->GetEntry(), 
		sSpellStore.LookupString(spellInfo->Name), targets.m_unitTarget);
#endif

	Spell *spell = new Spell(caster, spellInfo, false, NULL);
	spell->prepare(&targets);
}

SpellEntry *AIInterface::getSpellEntry(uint32 spellId)
{
	SpellEntry *spellInfo = sSpellStore.LookupEntry(spellId );

	if(!spellInfo)
	{
		sLog.outError("WORLD: unknown spell id %i\n", spellId);
		return NULL;
	}

	return spellInfo;
}

SpellCastTargets AIInterface::setSpellTargets(SpellEntry *spellInfo, Unit* target)
{
	SpellCastTargets targets;
	targets.m_unitTarget = target ? target->GetGUID() : 0;
	targets.m_itemTarget = 0;
	targets.m_srcX = 0;
	targets.m_srcY = 0;
	targets.m_srcZ = 0;
	targets.m_destX = 0;
	targets.m_destY = 0;
	targets.m_destZ = 0;

	if(m_nextSpell->spelltargetType == TTYPE_SINGLETARGET)
	{
		targets.m_targetMask = 2;
		targets.m_unitTarget = target->GetGUID();
	}
	else if(m_nextSpell->spelltargetType == TTYPE_SOURCE)
	{
		targets.m_targetMask = 32;
		targets.m_srcX = m_Unit->GetPositionX();
		targets.m_srcY = m_Unit->GetPositionY();
		targets.m_srcZ = m_Unit->GetPositionZ();
	}
	else if(m_nextSpell->spelltargetType == TTYPE_DESTINATION)
	{
		targets.m_targetMask = 64;
		targets.m_destX = target->GetPositionX();
		targets.m_destY = target->GetPositionY();
		targets.m_destZ = target->GetPositionZ();
	}
	else if(m_nextSpell->spelltargetType == TTYPE_CASTER)
	{
		targets.m_targetMask = 2;
		targets.m_unitTarget = m_Unit->GetGUID();
	}

	return targets;
}

AI_Spell *AIInterface::getSpell()
{
	/* limit spell query time to once every second */
	if((time_t)next_spell_time >= World::UNIXTIME)
		return 0;

	next_spell_time = World::UNIXTIME + 1;

	// look at our spells
	AI_Spell * sp;
	uint32 cast_time;
	AI_Spell * def_spell = 0;
	for(list<AI_Spell*>::iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
	{
        sp = *itr;
		if(sp->agent == AGENT_SPELL)
		{
			switch(sp->spellType)
			{
			case STYPE_BUFF:
				{
					// cast the buff at requested percent only if we don't have it already
					if(sp->procChance >= 100 || Rand(sp->procChance))
					{
						if(!m_Unit->HasActiveAura(sp->spell->Id))
						{
#ifdef _AI_DEBUG
							sLog.outString("AI DEBUG: Returning aura %s for unit %u", sSpellStore.LookupString( sp->spell->Name ),
								sp->entryId);
#endif
							return sp;
						}
					}
				}break;

			default:
				{
					if(def_spell)
						continue;

					// cast the spell at requested percent.
					if(sp->procChance >= 100 || Rand(sp->procChance))
					{
						if(m_Unit->GetUInt32Value(UNIT_FIELD_POWER1) < sp->spell->manaCost)
							continue;
#ifdef _AI_DEBUG
						sLog.outString("AI DEBUG: Returning spell %s for unit %u", sSpellStore.LookupString( sp->spell->Name ),
							sp->entryId);
#endif
						def_spell = sp;
					}
				}break;
			}
		}
	}

	if(def_spell)
	{
		// Check for cooldowns
		if(GetSpellCooldown(sp->spell->Id) > 0)
			return 0;

		cast_time = GetCastTime(sCastTime.LookupEntry( sp->spell->CastingTimeIndex ) );
		cast_time /= 1000;
		if(cast_time)
			next_spell_time = World::UNIXTIME + cast_time;

/*		// add the cooldown - added at actual cast
		AddSpellCooldown(sp->spell->Id);*/
		return def_spell;
	}

#ifdef _AI_DEBUG
	sLog.outString("AI DEBUG: Returning no spell for unit %u", m_Unit->GetEntry());
#endif
	return 0;
}

void AIInterface::addSpellToList(AI_Spell *sp)
{
	if(!sp->spell)
		return;

	m_spells.push_back(sp);
	m_Unit->m_SpellList.insert(sp->spell->Id); // add to list
}

uint32 AIInterface::getThreatByGUID(uint64 guid)
{
	Unit *obj = m_Unit->GetMapMgr()->GetUnit(guid);
	if(obj)
		return getThreatByPtr(obj);

	return 0;
}

uint32 AIInterface::getThreatByPtr(Unit* obj)
{
	TargetMap::iterator it = m_aiTargets.find(obj);
	if(it != m_aiTargets.end())
	{
		return it->second;
	}
	return 0;
}

Unit *AIInterface::GetMostHated()
{
	Unit *ResultUnit=NULL;

	//override mosthated with taunted target
	ResultUnit = getTauntedBy();
	if(ResultUnit)
		return ResultUnit;

	int32 threat = 0;

	TargetMap::iterator itr,itr2;
	for (itr = m_aiTargets.begin(); itr != m_aiTargets.end();) // Find Target and Cleanup Targetlist
	{
		itr2 = itr;
		++itr;
		if((itr2->second + itr2->first->GetThreatModifyer()) > threat) // threat may be < 0 due to some modifyers.
		{
/*
			bool cansee;
			if(m_nextTarget && m_nextTarget->GetInstanceID() == m_Unit->GetInstanceID())
			{
				if(m_Unit->GetTypeId() == TYPEID_UNIT)
					cansee=((Creature*)m_Unit)->CanSee(m_nextTarget);
				else
					cansee = ((Player*)m_Unit)->CanSee(m_nextTarget);
			}
			else 
				cansee=false; */ 
//			if(itr2->first->isAlive() )
			if(	!m_Unit->GetMapMgr()->GetUnit(m_nextTarget->GetGUID()) || 
				!m_nextTarget->isAlive()
//				!cansee || 
//				!isAttackable(m_Unit, m_nextTarget) //maybe our target is not attackable anymore : PVP
				)
			{
				m_aiTargets.erase(itr2);
			}
			else if((itr2->second + itr2->first->GetThreatModifyer())>threat)
			{
				ResultUnit = itr2->first;
				threat = (itr2->second + itr2->first->GetThreatModifyer());
			}
		}
	}
	m_currentHighestThreat = threat;

/*	if(ResultUnit && ResultUnit->GetInstanceID() == m_Unit->GetInstanceID())
	{
		m_Unit->SetUInt64Value(UNIT_FIELD_TARGET, target->GetGUID());
		return target;
	}*/
	return ResultUnit;
}

bool AIInterface::modThreatByGUID(uint64 guid, int32 mod)
{
	if (!m_aiTargets.size())
		return false;

	Unit *obj = m_Unit->GetMapMgr()->GetUnit(guid);
	if(obj)
		return modThreatByPtr(obj, mod);

	return false;
}

bool AIInterface::modThreatByPtr(Unit* obj, int32 mod)
{
	if(!obj)
		return false;
	TargetMap::iterator it = m_aiTargets.find(obj);
	if(it != m_aiTargets.end())
	{
		it->second += mod;
		if((it->second + obj->GetThreatModifyer()) > m_currentHighestThreat)
		{
			// new target!
			m_currentHighestThreat = it->second + obj->GetThreatModifyer();
			if(!isTaunted)
			{
				m_currentHighestThreat = it->second + obj->GetThreatModifyer();
				SetNextTarget(obj);
			}
		}
	}
	else
	{
		m_aiTargets.insert( make_pair( obj, mod ) );
		if((mod + obj->GetThreatModifyer()) > m_currentHighestThreat)
		{
			if(!isTaunted)
			{
				m_currentHighestThreat = mod + obj->GetThreatModifyer();
				SetNextTarget(obj);
			}
		}
	}

	if(obj == m_nextTarget)
	{
		// check for a possible decrease in threat.
		if(mod < 0)
		{
			SetNextTarget(FindTargetForSpell(m_nextSpell));
		}
	}
	return true;
}

void AIInterface::addAssistTargets(Unit* Friend)
{
	// stop adding stuff that gives errors on linux!
	m_assistTargets.insert(Friend);
}

void AIInterface::WipeHateList()
{
	for(TargetMap::iterator itr = m_aiTargets.begin(); itr != m_aiTargets.end(); ++itr)
		itr->second = 0;
}

void AIInterface::WipeTargetList()
{
	SetNextTarget(NULL);

	m_nextSpell = NULL;
	m_aiTargets.clear();
}

bool AIInterface::taunt(Unit* caster, bool apply)
{
	if(apply)
	{
		//wowwiki says that we cannot owerride this spell
		if(GetIsTaunted())
			return false;

		if(!caster)
		{
			isTaunted = false;
			return false;
		}

		//check if we can attack taunter. Maybe it's a hack or a bug if we fail this test
		if(isHostile(m_Unit, caster))
		{
			//check if we have to add him to our agro list
			GetMostHated(); //update our most hated list/ Note that at this point we do not have a taunter yet. If we would have then this funtion will not give real mosthated
			int32 oldthreat = getThreatByPtr(caster);
			//make sure we rush the target anyway. Since we are not tauted yet, this will also set our target
			modThreatByPtr(caster,abs(m_currentHighestThreat-oldthreat)+1); //we need to be the most hated at this moment
//			SetNextTarget(caster);
		}
		isTaunted = true;
		tauntedBy = caster;
	}
	else
	{
		isTaunted = false;
		tauntedBy = NULL;
		//taunt is ower, we should get a new target based on most hated list
		SetNextTarget(GetMostHated());
	}

	return true;
}

Unit* AIInterface::getTauntedBy()
{
	if(GetIsTaunted())
	{
		return tauntedBy;
	}
	else
	{
		return NULL;
	}
}

bool AIInterface::GetIsTaunted()
{
	if(isTaunted)
	{
		if(!tauntedBy || !tauntedBy->isAlive())
		{
			isTaunted = false;
			tauntedBy = NULL;
		}
	}
	return isTaunted;
}

void AIInterface::CheckTarget(Unit* target)
{
	if(target == UnitToFollow)		  // fix for crash here
	{
		UnitToFollow = NULL;
		m_lastFollowX = m_lastFollowY = 0;
	}

	if (target == m_nextTarget)	 // no need to cast on these.. mem addresses are still the same
	{
		SetNextTarget(NULL);
		m_nextSpell = NULL;
	}

	if(target == UnitToFear)
		UnitToFear = NULL;

	if(tauntedBy == target)
		tauntedBy = 0;

	AssistTargetSet::iterator  itr = m_assistTargets.find(target);
	if(itr != m_assistTargets.end())
		m_assistTargets.erase(itr);

	if (!m_aiTargets.size())
		return;

	TargetMap::iterator it2 = m_aiTargets.find(target);
	if(it2 != m_aiTargets.end())
	{
		m_aiTargets.erase(it2);
	}
}

uint32 AIInterface::_CalcThreat(uint32 damage, uint32 spellId, Unit* Attacker)
{
	int32 mod = 0;
	if(spellId != 0)
	{
		mod = objmgr.GetAIThreatToSpellId(spellId);
		if(mod == 0)
		{
			mod = damage;
		}
	}
	else
	{
		mod = damage;
	}

	// modify mod by Affects
	mod += (mod * Attacker->GetGeneratedThreatModifyer() / 100);

	return mod;
}

void AIInterface::WipeReferences()
{
	m_nextSpell = 0;
	m_aiTargets.clear();
	SetNextTarget(NULL);
	UnitToFear = 0;
	UnitToFollow = 0;
	tauntedBy = 0;
}

uint32 AIInterface::GetSpellCooldown(uint32 SpellId)
{
	map<uint32, uint32>::iterator itr = m_spellCooldown.find(SpellId);
	if(itr != m_spellCooldown.end())
	{
		uint32 mstime = getMSTime();
		uint32 cotime = itr->second;
		if(mstime > cotime)
		{
			// empty cooldown -> erase it
			m_spellCooldown.erase(itr);
			return 0;
		}
		else
			return cotime - mstime;
	}
	return 0;
}

void AIInterface::AddSpellCooldown(SpellEntry * pSpell, AI_Spell * sp)
{
	uint32 Cooldown = pSpell->CategoryRecoveryTime > pSpell->RecoveryTime ? pSpell->CategoryRecoveryTime : pSpell->RecoveryTime;
	m_spellCooldown[pSpell->Id] = getMSTime() + Cooldown;
}
